home主页和AiTools页
This commit is contained in:
117
components/Footer.vue
Normal file
117
components/Footer.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div id="footer-container">
|
||||
<IntegratedLayout>
|
||||
<div class="container">
|
||||
<div class="left-container">
|
||||
<img src="/logo/logo-rect.png" />
|
||||
<span>AIProdLaunch</span>
|
||||
</div>
|
||||
<div class="right-container">
|
||||
<div>
|
||||
<img src="/logo/bottom-logo.png" />
|
||||
</div>
|
||||
<div class="navigation-bottom">
|
||||
<span v-for="item in first">
|
||||
{{item.name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="navigation-bottom">
|
||||
<span v-for="item in two" @click="goto(item.path)">
|
||||
{{item.name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="bottom-span">
|
||||
All rights reserved ©2025 AIToolsFinder.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</IntegratedLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Footer",
|
||||
data() {
|
||||
return {
|
||||
first: [{
|
||||
name: 'Home'
|
||||
},
|
||||
{
|
||||
name: 'AI Daily News'
|
||||
},
|
||||
{
|
||||
name: 'AI Hub'
|
||||
},
|
||||
{
|
||||
name: 'Learn'
|
||||
},
|
||||
{
|
||||
name: 'About Us'
|
||||
}
|
||||
],
|
||||
two: [{
|
||||
name: 'Privacy Policy',
|
||||
path: '/privacy'
|
||||
},
|
||||
{
|
||||
name: 'Terms Of Service',
|
||||
path: '/service'
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goto(path) {
|
||||
this.$router.push(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#footer-container {
|
||||
width: 100%;
|
||||
height: $footerBarHeight;
|
||||
|
||||
.container {
|
||||
height: $footerBarHeight;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.left-container {
|
||||
@include flex-center;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
font-size: $larg-font-size;
|
||||
color: $main-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.right-container {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.bottom-span {
|
||||
color: $grey-color;
|
||||
}
|
||||
|
||||
.navigation-bottom {
|
||||
span {
|
||||
display: inline-block;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
164
components/Header.vue
Normal file
164
components/Header.vue
Normal file
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div id="header-container">
|
||||
<IntegratedLayout>
|
||||
<div class="navigation-container">
|
||||
<div class="logo">
|
||||
<img src="/logo/white-logo.png" />
|
||||
<span>AIProdLaunch</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="navigation-item pointer" v-for="item in navRoutes" :key="item.path"
|
||||
@click="handleParentClick(item)" @mouseenter="showSubmenu(item)" @mouseleave="hideSubmenu">
|
||||
<span
|
||||
:class="{ 'selected-navigation': $route.matched.some(record => record.path === item.path) }">{{
|
||||
item.meta.navigationName }}
|
||||
<i class="el-icon-arrow-down" v-if="item.meta.children"></i>
|
||||
</span>
|
||||
<div v-if="activeMenu === item.path && item.meta.children" class="submenu">
|
||||
<div v-for="sub in item.children" :key="sub.path" @click.stop="goto(sub.path)"
|
||||
class="submenu-item pointer">
|
||||
<img :src="`/logo/${sub.meta.icon}.png`" />
|
||||
{{ sub.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</IntegratedLayout>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
routes
|
||||
} from '../router';
|
||||
|
||||
export default {
|
||||
name: "Header",
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* 得到routes的meta里非hidden的路由
|
||||
*/
|
||||
navRoutes() {
|
||||
const r = routes.filter(e => {
|
||||
if (e.meta) {
|
||||
return !e.meta.hidden
|
||||
}
|
||||
return false
|
||||
})
|
||||
return r
|
||||
},
|
||||
|
||||
/**
|
||||
* 得到当前的路由path
|
||||
*/
|
||||
currentRoutePath() {
|
||||
return this.$route.path
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeMenu: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleParentClick(item) {
|
||||
// 只有没有子菜单时才跳转
|
||||
if (!item.children || item.children.length === 0) {
|
||||
this.goto(item.path);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 编程式导航
|
||||
* @param {String} path 导航的路径
|
||||
*/
|
||||
goto(path) {
|
||||
console.log(path)
|
||||
this.$router.push(path)
|
||||
},
|
||||
showSubmenu(item) {
|
||||
if (item.children) {
|
||||
this.activeMenu = item.path;
|
||||
}
|
||||
},
|
||||
hideSubmenu() {
|
||||
this.activeMenu = null;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#header-container {
|
||||
width: 100%;
|
||||
height: $navigationBarHeight;
|
||||
background: $header-backgroungd;
|
||||
|
||||
|
||||
.navigation-container {
|
||||
height: $navigationBarHeight;
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.logo {
|
||||
@include flex-center;
|
||||
color: $white;
|
||||
|
||||
span {
|
||||
font-size: $normal-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-item {
|
||||
margin-left: 20px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
color: $grey;
|
||||
|
||||
&:hover {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.selected-navigation {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
padding: 20px 10px;
|
||||
transform: translateX(-50%);
|
||||
background: $white;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
|
||||
&-item {
|
||||
white-space: nowrap; // 防止文字换行
|
||||
padding: 10px;
|
||||
color: #000000;
|
||||
@include display-flex;
|
||||
|
||||
img {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #F5F6F9;
|
||||
color: $main-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
components/IntegratedLayout.vue
Normal file
33
components/IntegratedLayout.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div id="sub-container">
|
||||
<el-row>
|
||||
<el-col :xs="0" :sm="1" :md="2" :lg="3" :xl="4">
|
||||
<div> </div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="22" :md="20" :lg="18" :xl="16">
|
||||
<!-- <div class="container-content"> -->
|
||||
<slot></slot>
|
||||
<!-- </div> -->
|
||||
</el-col>
|
||||
<el-col :xs="0" :sm="1" :md="2" :lg="3" :xl="4">
|
||||
<div> </div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#sub-container {
|
||||
.container-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// overflow: auto;
|
||||
}
|
||||
</style>
|
||||
105
components/Pagination.vue
Normal file
105
components/Pagination.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="pagination">
|
||||
<!-- 上一页按钮 -->
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="gotoPage(currentPage - 1)">
|
||||
<i class="el-icon-arrow-left"></i> </button>
|
||||
|
||||
<!-- 页面范围 -->
|
||||
<span v-for="pageNum in visiblePages" :key="pageNum"
|
||||
:class="['pagination-number', { active: currentPage === pageNum }]" @click="gotoPage(pageNum)">
|
||||
{{ pageNum }}
|
||||
</span>
|
||||
|
||||
<!-- 下一页按钮 -->
|
||||
<button class="pagination-btn" :disabled="currentPage === totalPages" @click="gotoPage(currentPage + 1)">
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
currentPage: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 1,
|
||||
},
|
||||
totalPages: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 跳转到指定页
|
||||
gotoPage(pageNum) {
|
||||
if (pageNum >= 1 && pageNum <= this.totalPages) {
|
||||
this.$emit("page-change", pageNum);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 计算显示的页码范围
|
||||
visiblePages() {
|
||||
const range = 5; // 显示的页码数量
|
||||
const half = Math.floor(range / 2);
|
||||
let start = this.currentPage - half;
|
||||
let end = this.currentPage + half;
|
||||
|
||||
// 调整范围,防止超出总页数
|
||||
if (start < 1) {
|
||||
start = 1;
|
||||
end = Math.min(range, this.totalPages);
|
||||
}
|
||||
if (end > this.totalPages) {
|
||||
end = this.totalPages;
|
||||
start = Math.max(this.totalPages - range + 1, 1);
|
||||
}
|
||||
|
||||
return Array.from({
|
||||
length: end - start + 1
|
||||
}, (_, i) => start + i);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pagination {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.pagination-btn,
|
||||
.pagination-number {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid #E2E8F0;
|
||||
border-radius: 6px;
|
||||
background-color: $white;
|
||||
color: $main-font-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination-number {
|
||||
@include flex-center;
|
||||
}
|
||||
|
||||
.pagination-btn:disabled {
|
||||
opacity: 0.5;
|
||||
color: #E2E8F0;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
.pagination-number.active {
|
||||
border-color: $main-color;
|
||||
color: $main-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user