hookehuyr

feat(router): 新增课程列表页面并更新搜索功能

在课程页面新增课程列表页面,并更新搜索栏功能以支持模糊搜索和页面跳转。搜索栏现在支持在课程页面跳转到课程列表页,并在课程列表页更新URL参数
......@@ -6,26 +6,62 @@
<input
type="text"
:placeholder="placeholder"
v-model="localValue"
class="bg-transparent outline-none flex-1 text-gray-700 placeholder-gray-400"
@input="handleSearch"
@blur="handleBlur"
/>
</FrostedGlass>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
import { defineProps, defineEmits, watch, ref } from 'vue'
import { useRouter } from 'vue-router'
import FrostedGlass from './FrostedGlass.vue'
const router = useRouter()
const props = defineProps({
placeholder: {
type: String,
default: '搜索'
},
modelValue: {
type: String,
default: ''
},
isCoursePage: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['search'])
const emit = defineEmits(['search', 'blur', 'update:modelValue'])
const localValue = ref(props.modelValue)
watch(() => props.modelValue, (newValue) => {
localValue.value = newValue
})
const handleSearch = () => {
emit('update:modelValue', localValue.value)
emit('search', localValue.value)
}
const handleSearch = (e) => {
emit('search', e.target.value)
const handleBlur = () => {
const query = localValue.value
emit('blur', query)
// 根据页面类型决定路由行为
if (props.isCoursePage) {
// 在课程页面,跳转到课程列表页
router.push({
path: '/courses-list',
query: { keyword: localValue.value }
})
} else {
// 在课程列表页,只更新URL参数
router.replace({ query: { ...router.currentRoute.value.query, keyword: localValue.value } })
}
}
</script>
......
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-21 13:06:38
* @LastEditTime: 2025-03-21 14:38:09
* @FilePath: /mlaj/src/router/index.js
* @Description: 文件描述
*/
......@@ -19,7 +19,7 @@ const routes = [
path: '/courses',
name: 'Courses',
component: () => import('../views/courses/CoursesPage.vue'),
meta: { title: '课程列表' },
meta: { title: '课程' },
},
{
path: '/courses/:id',
......@@ -34,6 +34,12 @@ const routes = [
meta: { title: '课程评价' }
},
{
path: '/courses-list',
name: 'CourseList',
component: () => import('../views/courses/CourseListPage.vue'),
meta: { title: '课程列表' }
},
{
path: '/profile',
name: 'Profile',
component: () => import('../views/profile/ProfilePage.vue'),
......
<!--
* @Date: 2025-03-21 14:31:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-21 14:54:47
* @FilePath: /mlaj/src/views/courses/CourseListPage.vue
* @Description: 文件描述
-->
<template>
<AppLayout title="课程列表">
<div class="pb-16">
<!-- Search Bar -->
<div class="pb-2">
<SearchBar placeholder="搜索" v-model="keyword" @search="handleSearch" @blur="handleBlur" />
</div>
<!-- Course List -->
<div class="px-4">
<div class="space-y-4">
<CourseCard v-for="course in courses" :key="course.id" :course="course" />
</div>
<!-- Load More -->
<div
v-if="hasMore"
class="py-4 text-center text-gray-500 text-sm"
@click="loadMore"
>
加载更多
</div>
<div
v-else
class="py-4 text-center text-gray-400 text-sm"
>
没有更多课程了
</div>
</div>
</div>
</AppLayout>
</template>
<script setup>
import { ref, onMounted, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
import AppLayout from '@/components/layout/AppLayout.vue';
import SearchBar from '@/components/ui/SearchBar.vue';
import CourseCard from '@/components/ui/CourseCard.vue';
import { courses as mockCourses } from '@/utils/mockData';
const $route = useRoute();
const courses = ref([]);
const hasMore = ref(true);
const page = ref(1);
const keyword = ref('');
// 搜索课程列表
const searchCourses = () => {
// 实际项目中这里会调用搜索API
const filteredCourses = keyword.value
? mockCourses.filter(course =>
(course.title?.toLowerCase().includes(keyword.value.toLowerCase()) ||
course.description?.toLowerCase().includes(keyword.value.toLowerCase())) ?? false
)
: [...mockCourses];
courses.value = filteredCourses;
hasMore.value = filteredCourses.length >= 10;
page.value = 1;
};
// 监听路由参数变化
watchEffect(() => {
const queryKeyword = $route.query.keyword;
if (keyword.value !== queryKeyword) {
keyword.value = queryKeyword || '';
searchCourses();
}
});
// Search handler
const handleSearch = (query) => {
keyword.value = query;
searchCourses();
};
// Blur handler
const handleBlur = (query) => {
keyword.value = query;
searchCourses();
};
// Load more courses
const loadMore = () => {
// 实际项目中这里会调用分页API
if (page.value < 3) {
const filteredCourses = keyword.value
? mockCourses.filter(course =>
course.title.toLowerCase().includes(keyword.value.toLowerCase()) ||
course.description.toLowerCase().includes(keyword.value.toLowerCase())
)
: [...mockCourses];
courses.value = [...courses.value, ...filteredCourses];
console.warn(courses.value);
page.value += 1;
hasMore.value = page.value < 3;
} else {
hasMore.value = false;
}
};
</script>
......@@ -3,7 +3,7 @@
<div class="pb-16">
<!-- Search Bar -->
<div class="pb-2">
<SearchBar placeholder="搜索" @search="handleSearch" />
<SearchBar placeholder="搜索" @search="handleSearch" @blur="handleBlur" :isCoursePage="true" />
</div>
<!-- Featured Course Banner -->
......@@ -52,7 +52,7 @@
<div class="px-4 mb-4">
<div class="flex justify-between items-center mb-2">
<h2 class="font-medium">超值线上课</h2>
<router-link to="#" class="text-xs text-gray-500 flex items-center">
<router-link to="/courses-list" class="text-xs text-gray-500 flex items-center">
更多
<svg
xmlns="http://www.w3.org/2000/svg"
......@@ -98,6 +98,11 @@ const handleSearch = (query) => {
console.log("Searching for:", query);
// Would implement actual search logic here
};
// Blur handler
const handleBlur = (query) => {
// 实际项目中这里会调用搜索API
console.log('Searching for:', query);
};
// Current time for the countdown timer
const todayDate = new Date();
......