feat:搜索结果展示
This commit is contained in:
parent
7551571f0a
commit
0c638147f2
BIN
public/serch/背景.png
Normal file
BIN
public/serch/背景.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 MiB |
@ -286,8 +286,85 @@ export class CourseApi {
|
||||
}
|
||||
|
||||
// 搜索课程
|
||||
static searchCourses(params: SearchRequest): Promise<ApiResponse<PaginationResponse<Course>>> {
|
||||
return ApiRequest.get('/courses/search', params)
|
||||
static async searchCourses(params: {
|
||||
keyword?: string
|
||||
limit?: string
|
||||
page?: number
|
||||
}): Promise<ApiResponse<Course[]> & { total?: number }> {
|
||||
try {
|
||||
console.log('🔍 搜索课程:', params)
|
||||
|
||||
const queryParams: any = {}
|
||||
if (params.keyword) queryParams.keyword = params.keyword
|
||||
if (params.limit) queryParams.limit = params.limit
|
||||
|
||||
const response = await ApiRequest.get<any>('/aiol/index/search', queryParams)
|
||||
console.log('✅ 搜索课程成功:', response)
|
||||
|
||||
// 处理后端响应格式
|
||||
if (response.data && response.data.success && response.data.result) {
|
||||
// 转换后端数据格式为前端格式
|
||||
const courses: Course[] = response.data.result.map((item: BackendCourseItem) => ({
|
||||
id: item.id,
|
||||
title: item.name || '',
|
||||
description: item.description || '',
|
||||
instructor: item.school || '未知讲师',
|
||||
teacherList: item.teacherList || [],
|
||||
duration: item.arrangement || '待定',
|
||||
level: this.mapDifficultyToLevel(item.difficulty),
|
||||
category: item.subject || '其他',
|
||||
thumbnail: item.cover || '',
|
||||
price: 0,
|
||||
rating: 0,
|
||||
studentsCount: item.enrollCount || 0,
|
||||
lessonsCount: 0,
|
||||
tags: [],
|
||||
isEnrolled: item.isEnrolled || false,
|
||||
progress: 0,
|
||||
createdAt: this.formatTimestamp(item.createTime),
|
||||
updatedAt: this.formatTimestamp(item.updateTime),
|
||||
status: item.status === 1 ? 'published' : 'draft',
|
||||
enrollmentCount: item.enrollCount || 0,
|
||||
maxEnrollment: item.maxEnroll || 0,
|
||||
startDate: item.startTime || '',
|
||||
endDate: item.endTime || '',
|
||||
outline: item.outline || '',
|
||||
prerequisite: item.prerequisite || '',
|
||||
reference: item.reference || '',
|
||||
target: item.target || '',
|
||||
question: item.question || '',
|
||||
video: item.video || '',
|
||||
izAi: item.izAi,
|
||||
// 添加AI伴学相关字段
|
||||
hasAiCompanion: item.izAi === 1,
|
||||
aiEnabled: item.izAi === 1,
|
||||
instructors: item.teacherList || []
|
||||
}))
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: '搜索成功',
|
||||
data: courses,
|
||||
total: courses.length
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ 搜索API返回格式异常:', response)
|
||||
return {
|
||||
code: 500,
|
||||
message: response.data?.message || '搜索失败',
|
||||
data: [],
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('❌ 搜索课程失败:', error)
|
||||
return {
|
||||
code: 500,
|
||||
message: error.message || '搜索失败',
|
||||
data: [],
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取热门课程
|
||||
|
@ -558,6 +558,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: Courses,
|
||||
meta: { title: '课程列表' }
|
||||
},
|
||||
{
|
||||
path: '/search',
|
||||
name: 'SearchResults',
|
||||
component: () => import('@/views/SearchResults.vue'),
|
||||
meta: { title: '搜索结果' }
|
||||
},
|
||||
{
|
||||
path: '/course/:id',
|
||||
name: 'CourseDetail',
|
||||
|
716
src/views/SearchResults.vue
Normal file
716
src/views/SearchResults.vue
Normal file
@ -0,0 +1,716 @@
|
||||
<template>
|
||||
<div class="search-results-page">
|
||||
<!-- 背景图片 -->
|
||||
<div class="background-container">
|
||||
<img src="/serch/背景.png" alt="背景" class="background-image" />
|
||||
</div>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<div class="main-content">
|
||||
<div class="container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">资源库·搜索</h1>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-container">
|
||||
<div class="search-box">
|
||||
<input
|
||||
v-model="searchKeyword"
|
||||
type="text"
|
||||
placeholder="搜索课程..."
|
||||
class="search-input"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<button class="search-btn" @click="handleSearch">
|
||||
<img
|
||||
src="https://lanhu-oss-proxy.lanhuapp.com/SketchPng4617c5d0e102114051f8321ef18af957e78bb797ad196789befab13c980617fc"
|
||||
alt="搜索"
|
||||
class="search-icon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果统计 -->
|
||||
<div class="search-stats" v-if="searchResults.length > 0 || hasSearched">
|
||||
<span class="stats-text">显示结果:{{ searchResults.length }}</span>
|
||||
<div class="sort-dropdown">
|
||||
<span>筛选</span>
|
||||
<span class="dropdown-icon">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<n-spin size="large" />
|
||||
<p>正在搜索...</p>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<div class="search-results" v-else-if="searchResults.length > 0">
|
||||
<!-- 课程网格 -->
|
||||
<div class="courses-grid">
|
||||
<div class="course-card" v-for="course in searchResults" :key="course.id">
|
||||
<div class="course-image">
|
||||
<img :src="course.thumbnail" :alt="course.title" />
|
||||
<!-- AI伴学标签 -->
|
||||
<div v-if="shouldShowAiTag(course)" class="ai-companion-tag">
|
||||
<img src="/images/aiCompanion/AI伴学标签@2x.png" alt="AI伴学" class="ai-tag-image">
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-info">
|
||||
<h3 class="course-title">{{ getCourseTitle(course) }}</h3>
|
||||
<div class="course-meta">
|
||||
<div class="course-chapters">
|
||||
<img src="/images/courses/课程总章数.png" alt="课程章节" class="meta-icon">
|
||||
<span>共9章54节</span>
|
||||
</div>
|
||||
<div class="course-duration">
|
||||
<img src="/images/courses/课程总时长.png" alt="课程时长" class="meta-icon">
|
||||
<span>12小时43分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-footer">
|
||||
<div class="course-stats">
|
||||
<span class="course-students">讲师: {{ getCourseInstructors(course) }}</span>
|
||||
</div>
|
||||
<button
|
||||
:class="getButtonClass(course)"
|
||||
@click="goToCourseDetail(course)"
|
||||
>
|
||||
<img
|
||||
v-if="shouldShowButtonIcon(course)"
|
||||
:src="getButtonIcon(course)"
|
||||
alt="AI图标"
|
||||
class="button-icon"
|
||||
/>
|
||||
{{ getButtonText(course) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无搜索结果 -->
|
||||
<div v-else-if="hasSearched && !loading" class="no-results">
|
||||
<div class="no-results-content">
|
||||
<div class="no-results-icon">🔍</div>
|
||||
<h3>未找到相关课程</h3>
|
||||
<p>请尝试其他关键词或检查拼写</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 初始状态 -->
|
||||
<div v-else class="initial-state">
|
||||
<div class="initial-content">
|
||||
<div class="placeholder-icon">🔍</div>
|
||||
<h3>输入关键词搜索课程</h3>
|
||||
<p>发现更多优质学习资源</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="searchResults.length > 0" class="pagination-container">
|
||||
<n-pagination
|
||||
v-model:page="currentPage"
|
||||
:page-count="totalPages"
|
||||
:page-size="pageSize"
|
||||
show-size-picker
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
@update:page="handlePageChange"
|
||||
@update:page-size="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { CourseApi } from '@/api'
|
||||
import type { Course } from '@/api/types'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 响应式数据
|
||||
const searchKeyword = ref('')
|
||||
const searchResults = ref<Course[]>([])
|
||||
const loading = ref(false)
|
||||
const hasSearched = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const totalResults = ref(0)
|
||||
|
||||
// 计算属性
|
||||
const totalPages = computed(() => Math.ceil(totalResults.value / pageSize.value))
|
||||
|
||||
// 搜索方法
|
||||
const handleSearch = async () => {
|
||||
if (!searchKeyword.value.trim()) {
|
||||
message.warning('请输入搜索关键词')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
hasSearched.value = true
|
||||
|
||||
try {
|
||||
console.log('🔍 开始搜索:', searchKeyword.value)
|
||||
|
||||
const response = await CourseApi.searchCourses({
|
||||
keyword: searchKeyword.value.trim(),
|
||||
limit: pageSize.value.toString(),
|
||||
page: currentPage.value
|
||||
})
|
||||
|
||||
console.log('📊 搜索结果:', response)
|
||||
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
searchResults.value = response.data || []
|
||||
totalResults.value = response.total || searchResults.value.length
|
||||
|
||||
// 更新URL参数
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
keyword: searchKeyword.value,
|
||||
page: currentPage.value.toString()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.error(response.message || '搜索失败')
|
||||
searchResults.value = []
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('❌ 搜索失败:', error)
|
||||
message.error('搜索失败,请稍后重试')
|
||||
searchResults.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
const handlePageChange = (page: number) => {
|
||||
currentPage.value = page
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const handlePageSizeChange = (size: number) => {
|
||||
pageSize.value = size
|
||||
currentPage.value = 1
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
// 课程相关方法(复用自courses页面)
|
||||
const shouldShowAiTag = (course: any) => {
|
||||
return course.hasAiCompanion || course.aiEnabled || course.izAi === 1
|
||||
}
|
||||
|
||||
const getCourseTitle = (course: any) => {
|
||||
return course.title || course.name || '课程标题'
|
||||
}
|
||||
|
||||
const getCourseInstructors = (course: any) => {
|
||||
if (course.instructors && course.instructors.length > 0) {
|
||||
return course.instructors.map((instructor: any) => instructor.name).join(', ')
|
||||
}
|
||||
if (course.teacherList && course.teacherList.length > 0) {
|
||||
return course.teacherList.map((teacher: any) => teacher.name).join(', ')
|
||||
}
|
||||
return course.instructor || course.school || '未知讲师'
|
||||
}
|
||||
|
||||
// 获取按钮文本(复用courses页面逻辑)
|
||||
const getButtonText = (course: any) => {
|
||||
const isAi = course?.izAi === 1
|
||||
const isEnrolled = course?.isEnrolled === true
|
||||
|
||||
console.log('🔍 按钮文本逻辑:', {
|
||||
courseId: course?.id,
|
||||
courseName: course?.title || course?.name,
|
||||
isAi,
|
||||
isEnrolled,
|
||||
izAi: course?.izAi
|
||||
})
|
||||
|
||||
if (isAi) {
|
||||
// AI伴学模式:isEnrolled=true显示"去学习",isEnrolled=false显示"去报名"
|
||||
return isEnrolled ? '去学习' : '去报名'
|
||||
} else {
|
||||
// 普通模式:isEnrolled=true显示"去学习",isEnrolled=false显示"去报名"
|
||||
return isEnrolled ? '去学习' : '去报名'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取按钮样式类(复用courses页面逻辑)
|
||||
const getButtonClass = (course: any) => {
|
||||
const isAi = course?.izAi === 1
|
||||
return isAi ? 'enroll-btn ai-btn' : 'enroll-btn'
|
||||
}
|
||||
|
||||
// 是否显示按钮图标(复用courses页面逻辑)
|
||||
const shouldShowButtonIcon = (course: any) => {
|
||||
return course?.izAi === 1
|
||||
}
|
||||
|
||||
// 获取按钮图标(复用courses页面逻辑)
|
||||
const getButtonIcon = (course: any) => {
|
||||
const isEnrolled = course?.isEnrolled === true
|
||||
// isEnrolled=true时显示courseAi.png,isEnrolled=false时显示courseAii.png
|
||||
return isEnrolled ? '/images/course/courseAi.png' : '/images/course/courseAii.png'
|
||||
}
|
||||
|
||||
// 跳转到课程详情页(复用courses页面逻辑)
|
||||
const goToCourseDetail = async (course: any) => {
|
||||
try {
|
||||
// 检查用户是否已登录
|
||||
if (!userStore.isLoggedIn) {
|
||||
console.log('用户未登录,跳转到AI伴学页面')
|
||||
router.push(`/ai-companion?courseId=${course.id}`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log('检查课程报名状态,课程ID:', course.id)
|
||||
|
||||
// 调用报名状态检查接口
|
||||
const response = await CourseApi.checkEnrollmentStatus(String(course.id))
|
||||
|
||||
if ((response.code === 0 || response.code === 200) && response.data) {
|
||||
const isEnrolled = response.data.result
|
||||
|
||||
if (isEnrolled) {
|
||||
// 已报名,跳转到已兑换页面
|
||||
console.log('用户已报名,跳转到已兑换页面')
|
||||
router.push(`/course/${course.id}/exchanged`)
|
||||
} else {
|
||||
// 未报名,跳转到AI伴学页面
|
||||
console.log('用户未报名,跳转到AI伴学页面')
|
||||
router.push(`/ai-companion?courseId=${course.id}`)
|
||||
}
|
||||
} else {
|
||||
// 查询失败,默认跳转到AI伴学页面
|
||||
console.warn('查询报名状态失败,跳转到AI伴学页面')
|
||||
router.push(`/ai-companion?courseId=${course.id}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查报名状态时发生错误:', error)
|
||||
// 发生错误时,默认跳转到AI伴学页面
|
||||
router.push(`/ai-companion?courseId=${course.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时处理URL参数
|
||||
onMounted(() => {
|
||||
const keyword = (route.query.keyword || route.query.q) as string
|
||||
const page = parseInt(route.query.page as string) || 1
|
||||
|
||||
if (keyword) {
|
||||
searchKeyword.value = keyword
|
||||
currentPage.value = page
|
||||
handleSearch()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-results-page {
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
background: #F5F8FB;
|
||||
}
|
||||
|
||||
.background-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.background-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 1420px;
|
||||
margin: 0 auto;
|
||||
padding: 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
padding: 0 60px 0 20px;
|
||||
border: 1px solid #E6E6E6;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
background: white;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: #0288D1;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.search-stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stats-text {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.sort-dropdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 0;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.courses-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
|
||||
column-gap: 20px;
|
||||
row-gap: 24px;
|
||||
margin-bottom: 40px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.course-card {
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
cursor: pointer;
|
||||
width: 268px;
|
||||
height: 350px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.course-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.course-image {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px 8px 0 0;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.course-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* AI伴学标签样式 */
|
||||
.ai-companion-tag {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ai-tag-image {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 60px;
|
||||
max-height: 30px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.course-info {
|
||||
padding: 16px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.course-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 0 0 12px 0;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.course-meta {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
gap: 10px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.course-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.course-chapters,
|
||||
.course-duration {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.meta-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.course-students {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.enroll-btn {
|
||||
background: #0286D5;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 10px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: background 0.2s;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.enroll-btn:hover {
|
||||
background: #40a9ff;
|
||||
}
|
||||
|
||||
/* AI伴学按钮样式 */
|
||||
.enroll-btn.ai-btn {
|
||||
background: linear-gradient(135deg, #33C4FF 0%, #0088D1 100%);
|
||||
}
|
||||
|
||||
.enroll-btn.ai-btn:hover {
|
||||
background: linear-gradient(135deg, #2bb3ff 0%, #0077b8 100%);
|
||||
}
|
||||
|
||||
/* 按钮图标样式 */
|
||||
.button-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
object-fit: contain;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px; /* 向下调整图标位置 */
|
||||
}
|
||||
|
||||
.no-results,
|
||||
.initial-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.no-results-content,
|
||||
.initial-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-results-icon,
|
||||
.placeholder-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 24px;
|
||||
opacity: 0.6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.no-results-content h3,
|
||||
.initial-content h3 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.no-results-content p,
|
||||
.initial-content p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (min-width: 1600px) {
|
||||
.courses-grid {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
.container {
|
||||
max-width: 1420px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
.courses-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1420px) {
|
||||
.container {
|
||||
max-width: calc(100vw - 160px);
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.courses-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
max-width: calc(100vw - 40px);
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.courses-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
column-gap: 16px;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
.search-stats {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.courses-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user