781 lines
17 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="faculty-page">
<!-- 横幅图片区域 -->
<div class="page-header">
<div class="banner-image-container">
<!-- 实际图片 -->
<!-- 图片文字叠加层 -->
<div class="banner-text-overlay">
<h2 class="banner-title">师资力量</h2>
<p class="banner-subtitle">涵盖20+顶尖专家</p>
</div>
<img v-if="hasBannerImage" :src="bannerImageSrc" alt="师资力量横幅" class="banner-image" />
<!-- 图片占位区域 -->
<div v-else class="banner-placeholder">
<div class="placeholder-content">
<div class="placeholder-icon">🖼</div>
<div class="placeholder-text">师资力量横幅图片占位</div>
<div class="placeholder-desc">请提供横幅图片</div>
</div>
</div>
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<div class="container">
<!-- 筛选标签栏 -->
<div class="filter-tabs">
<div>擅长领域</div>
<button v-for="tab in filterTabs" :key="tab.id" :class="['filter-tab', { active: activeTab === tab.id }]"
@click="activeTab = tab.id">
{{ tab.name }}
</button>
</div>
<!-- 师资卡片网格 -->
<div class="faculty-grid">
<div v-for="teacher in paginatedTeachers" :key="teacher.id" class="faculty-card" @click="navigateToTeacherDetail(teacher.id)">
<div class="card-header">
<div class="avatar-container"
@mouseenter="showCourseInfo(teacher.id)"
@mouseleave="hideCourseInfo(teacher.id)">
<!-- 师资头像 -->
<img :src="teacher.avatar" :alt="teacher.name" class="teacher-avatar" />
<div v-if="teacher.featured" class="featured-badge"></div>
<!-- 课程介绍盒子 -->
<div class="course-info-box" :class="{ active: expandedTeacherId === teacher.id }">
<div class="course-info-content">
<h4 class="course-title">课程介绍</h4>
<p class="course-description">主讲课程为Python语言程序设计动画原理与实现虚拟现实技术与应用</p>
<div class="course-meta">{{ teacher?.courseCount || 5 }}</div>
<div class="course-arrow"></div>
</div>
</div>
</div>
<div class="card-arrow" @click="navigateToTeacherDetail(teacher.id)">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</div>
<div class="card-arrowhead" v-if="expandedTeacherId === teacher.id" @click.stop="navigateToTeacherDetail(teacher.id)"></div>
</div>
<div class="card-content">
<h3 class="teacher-name">{{ teacher.name }}</h3>
<p class="teacher-position">{{ teacher.position }}</p>
<p class="teacher-description">{{ teacher.description }}</p>
<div class="teacher-specialty">{{ teacher.specialty }}</div>
</div>
</div>
</div>
<!-- 分页组件 -->
<div class="pagination">
<button class="page-btn" :disabled="currentPage === 1" @click="goToPage(currentPage - 1)">
上一页
</button>
<button v-for="page in visiblePages" :key="page" :class="['page-btn', { active: currentPage === page }]"
@click="typeof page === 'number' ? goToPage(page) : null" :disabled="typeof page !== 'number'">
{{ page }}
</button>
<button class="page-btn" :disabled="currentPage === totalPages" @click="goToPage(currentPage + 1)">
下一页
</button>
<span class="page-info">{{ totalPages }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
// 为教师项添加明确类型,包含可选的 courseCount 字段
interface Teacher {
id: number
name: string
position: string
description: string
specialty: string
avatar: string
featured?: boolean
courseCount?: number
}
// 横幅图片相关
const bannerImageSrc = ref('/images/Teachers/师资力量切图-轮播区.png')
const hasBannerImage = computed(() => bannerImageSrc.value.trim() !== '')
// 设置横幅图片的方法(供后续使用)
// const setBannerImage = (imagePath: string) => {
// bannerImageSrc.value = imagePath
// }
// 初始化路由
const router = useRouter()
// 展开的教师ID
const expandedTeacherId = ref<number | null>(null)
// 跳转到讲师详情页
const navigateToTeacherDetail = (teacherId: number) => {
router.push(`/teacher/${teacherId}`)
}
// 处理箭头点击
// const toggleCourseInfo = (teacherId: number) => {
// if (expandedTeacherId.value === teacherId) {
// expandedTeacherId.value = null
// } else {
// expandedTeacherId.value = teacherId
// }
// }
// 鼠标悬停显示课程信息
const showCourseInfo = (teacherId: number) => {
expandedTeacherId.value = teacherId
}
// 鼠标离开隐藏课程信息
const hideCourseInfo = (_: number) => {
expandedTeacherId.value = null
}
// 筛选标签数据
const filterTabs = ref([
{ id: 'all', name: '全部' },
{ id: 'chinese-promotion', name: '汉语国际推广' },
{ id: 'chinese-language', name: '汉语言' },
{ id: 'chinese-education', name: '华文教育' },
{ id: 'e-commerce', name: '电子商务' },
{ id: 'new-energy', name: '新能源' },
{ id: 'smart-education', name: '智慧教育' }
])
const activeTab = ref('all')
// 师资数据
const teachers = ref<Teacher[]>([
{
id: 1,
name: '黄天羽',
position: '北京理工大学计算机学院教授',
description: '北京市高等学校育年教学名师,博导',
specialty: '主讲 - 软件工程基础训练',
featured: true,
avatar: '/images/Teachers/师资力量1.png'
},
{
id: 2,
name: '蓝天',
position: '北京理工大学MBA企业文化专家顾问',
description: '多家知名上市企业高管',
specialty: '主讲 - MBA企业文化专家',
avatar: '/images/Teachers/师资力量2.png'
},
{
id: 3,
name: '万精云',
position: '中国人事科学研究院研究员',
description: '中国科学院博士',
specialty: '主讲 - 人事管理专家',
avatar: '/images/Teachers/师资力量3.png'
},
{
id: 4,
name: '张庆勋',
position: '北京大学博士',
description: '内蒙古财经大学教授',
specialty: '主讲 - 金融学专家',
avatar: '/images/Teachers/师资力量4.png'
},
{
id: 5,
name: '程毅',
position: '中国科技大学博士研究生导师',
description: '计算机科学与技术专业带头人',
specialty: '主讲 - 计算机科学专家',
avatar: '/images/Teachers/师资力量5.png'
},
{
id: 6,
name: '王德华',
position: '数字经济与金融研究中心专家',
description: '多家知名上市企业高级管理顾问',
specialty: '主讲 - 数字经济专家',
avatar: '/images/Teachers/师资力量6.png'
},
{
id: 7,
name: '马前程',
position: '清华大学管理学院教授',
description: '多家一线互联网企业高级管理顾问',
specialty: '主讲 - 企业管理专家',
avatar: '/images/Teachers/师资力量7.png'
},
{
id: 8,
name: '陈宇',
position: '知名上市企业高级管理顾问专家',
description: '多家一线互联网企业高级管理顾问',
specialty: '主讲 - 企业战略专家',
avatar: '/images/Teachers/师资力量8.png'
}
])
// 分页相关
const currentPage = ref(1)
const pageSize = 8
const totalPages = computed(() => Math.ceil(teachers.value.length / pageSize))
const paginatedTeachers = computed(() => {
const start = (currentPage.value - 1) * pageSize
const end = start + pageSize
return teachers.value.slice(start, end)
})
const visiblePages = computed(() => {
const pages = []
const total = totalPages.value
const current = currentPage.value
if (total <= 7) {
for (let i = 1; i <= total; i++) {
pages.push(i)
}
} else {
if (current <= 4) {
for (let i = 1; i <= 5; i++) {
pages.push(i)
}
pages.push('...')
pages.push(total)
} else if (current >= total - 3) {
pages.push(1)
pages.push('...')
for (let i = total - 4; i <= total; i++) {
pages.push(i)
}
} else {
pages.push(1)
pages.push('...')
for (let i = current - 1; i <= current + 1; i++) {
pages.push(i)
}
pages.push('...')
pages.push(total)
}
}
return pages
})
const goToPage = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
}
}
</script>
<style scoped>
.faculty-page {
min-height: 100vh;
background: #f6f6f6;
}
/* 横幅图片区域 */
.page-header {
width: 100%;
position: relative;
overflow: hidden;
}
.banner-image-container {
width: 100%;
height: auto;
position: relative;
}
.banner-text-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #000;
z-index: 10;
padding: 20px;
font-weight: 600;
}
.banner-title {
font-size: 32px;
margin: 0 0 8px 0;
}
.banner-subtitle {
font-size: 14px;
font-weight: 500;
margin: 0;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
/* 响应式调整 */
@media (max-width: 768px) {
.banner-title {
font-size: 28px;
}
.banner-subtitle {
font-size: 16px;
}
}
@media (max-width: 480px) {
.banner-title {
font-size: 24px;
}
.banner-subtitle {
font-size: 14px;
}
}
.banner-image {
width: 100%;
height: auto;
display: block;
object-fit: cover;
}
.banner-placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.banner-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
}
.placeholder-content {
text-align: center;
color: white;
position: relative;
z-index: 1;
}
.placeholder-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.8;
}
.placeholder-text {
font-size: 24px;
font-weight: 600;
margin-bottom: 8px;
}
.placeholder-desc {
font-size: 16px;
opacity: 0.8;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 主要内容区域 */
.main-content {
padding: 40px 0 80px;
background-color: #fff;
}
/* 筛选标签栏 */
.filter-tabs {
display: flex;
align-items: center;
gap: 0;
margin-bottom: 40px;
padding-bottom: 30px;
background: transparent;
border-bottom: 1px solid #e5e5e5;
box-shadow: none;
}
.filter-tab {
margin-right: 25px;
/* margin-bottom: 30px; */
padding: 7px 10px;
border: none;
background: transparent;
color: #333333;
font-size: 14px;
cursor: pointer;
border-radius: 0;
transition: all 0.3s;
white-space: nowrap;
position: relative;
border-bottom: 2px solid transparent;
}
.filter-tabs div {
padding: 7px 10px 7px 0;
margin-right: 10px;
}
.filter-tab:hover {
color: #4A90E2;
}
.filter-tab.active {
background: #EEF9FF;
color: #0088D1;
border-radius: 8px;
}
/* 师资卡片网格 */
.faculty-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 40px;
}
.faculty-card {
background: white;
overflow: visible;
transition: all 0.3s;
cursor: pointer;
aspect-ratio: 3/4;
position: relative;
}
.faculty-card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
.card-header {
position: relative;
height: 70%;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
overflow: hidden;
}
.avatar-container {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
overflow: visible;
}
.teacher-avatar {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.featured-badge {
position: absolute;
top: 0;
left: 0;
background: #FF4419;
width: 96px;
height: 37px;
background-image: url('/images/Teachers/excellent-lecturer.png');
background-size: 100% 100%;
}
.card-arrow {
position: absolute;
bottom: 0;
right: 0;
color: white;
background: #0088D1;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
cursor: pointer;
}
.card-arrowhead {
position: absolute;
bottom: 5px;
right: 20px;
color: white;
background: #0088D1;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
background-image: url('/images/Teachers/arrowhead.png');
background-size: 100% 100%;
}
.card-arrow:hover {
background: #357ABD;
transform: scale(1.1);
}
.card-content {
padding: 16px;
height: 30%;
display: flex;
flex-direction: column;
justify-content: flex-start;
text-align: left;
}
.teacher-name {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0 0 6px 0;
line-height: 1.2;
}
.teacher-position {
font-size: 12px;
color: #666;
margin: 0 0 4px 0;
line-height: 1.3;
}
.teacher-description {
font-size: 12px;
color: #666;
margin: 0 0 6px 0;
line-height: 1.3;
}
.teacher-specialty {
font-size: 11px;
color: #999;
line-height: 1.3;
}
/* 课程介绍盒子样式 */
.course-info-box {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: #0088D1;
color: white;
padding: 16px;
border-radius: 0 0 5px 5px;
/* box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); */
z-index: 10;
transform: translateY(100%);
opacity: 0;
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.course-info-box.active {
transform: translateY(0);
opacity: 1;
}
.course-info-content {
position: relative;
}
.course-title {
font-size: 18px;
margin: 0 0 8px 0;
}
.course-description {
font-size: 14px;
line-height: 1.4;
margin: 0 0 12px 0;
}
.course-meta {
font-size: 12px;
opacity: 0.8;
display: flex;
justify-content: space-between;
align-items: center;
}
/* .course-arrow {
position: absolute;
top: -8px;
right: 16px;
width: 16px;
height: 16px;
background: #4A90E2;
transform: rotate(45deg);
box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.1);
} */
/* 移除冲突的hover样式改用JavaScript控制 */
/* 箭头按钮旋转动画 */
/* .card-arrow {
transition: transform 0.3s ease;
} */
/* .faculty-card:hover .card-arrow {
transform: rotate(180deg);
} */
/* 分页组件 */
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin-top: 40px;
}
.page-btn {
padding: 8px 16px;
border: 1px solid #ddd;
background: white;
color: #666;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
min-width: 40px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
}
.page-btn:hover:not(:disabled) {
background: #f8f9fa;
border-color: #4A90E2;
color: #4A90E2;
}
.page-btn.active {
background: #4A90E2;
border-color: #4A90E2;
color: white;
}
.page-btn:disabled {
background: #f5f5f5;
color: #ccc;
cursor: not-allowed;
border-color: #eee;
}
.page-info {
margin-left: 16px;
color: #666;
font-size: 14px;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.faculty-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 1024px) {
.placeholder-icon {
font-size: 40px;
}
.placeholder-text {
font-size: 20px;
}
}
@media (max-width: 768px) {
.faculty-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.filter-tabs {
flex-wrap: wrap;
gap: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.filter-tab {
padding: 10px 16px;
font-size: 13px;
flex-shrink: 0;
}
.placeholder-icon {
font-size: 36px;
}
.placeholder-text {
font-size: 18px;
}
.placeholder-desc {
font-size: 14px;
}
}
@media (max-width: 480px) {
.faculty-grid {
grid-template-columns: 1fr;
}
.container {
padding: 0 16px;
}
.main-content {
padding: 20px 0 40px;
}
.placeholder-icon {
font-size: 32px;
}
.placeholder-text {
font-size: 16px;
}
.placeholder-desc {
font-size: 12px;
}
}
</style>