hookehuyr

feat(课程评价): 添加课程评价页面及路由

新增课程评价页面,包含评分和评论列表功能。同时更新路由配置,支持从课程详情页跳转到评价页面
......@@ -21,5 +21,7 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
SearchBar: typeof import('./components/ui/SearchBar.vue')['default']
SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default']
VanList: typeof import('vant/es')['List']
VanRate: typeof import('vant/es')['Rate']
}
}
......
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-21 10:27:02
* @LastEditTime: 2025-03-21 11:33:55
* @FilePath: /mlaj/src/router/index.js
* @Description: 文件描述
*/
......@@ -15,48 +15,96 @@ const routes = [
meta: { title: '首页' },
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/courses',
name: 'Courses',
component: () => import('../views/courses/CoursesPage.vue'),
meta: { title: '课程列表' },
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/courses/:id',
name: 'CourseDetail',
component: () => import('../views/courses/CourseDetailPage.vue'),
meta: { title: '课程详情' },
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/profile',
name: 'Profile',
component: () => import('../views/profile/ProfilePage.vue'),
meta: { title: '个人中心' },
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/login',
name: 'Login',
component: () => import('../views/auth/LoginPage.vue'),
meta: { title: '登录' }
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/register',
name: 'Register',
component: () => import('../views/auth/RegisterPage.vue'),
meta: { title: '注册' }
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/forgotPwd',
name: 'ForgotPassword',
component: () => import('../views/auth/ForgotPasswordPage.vue'),
meta: { title: '忘记密码' }
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/activities',
name: 'Activities',
component: () => import('../views/activities/ActivitiesPage.vue'),
meta: { title: '活动列表' }
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/activities/:id',
name: 'ActivityDetail',
component: () => import('../views/activities/ActivityDetailPage.vue'),
......@@ -64,6 +112,12 @@ const routes = [
meta: { title: '活动详情' }
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/activities/:id/signup',
name: 'ActivitySignup',
component: () => import('../views/activities/ActivitySignupPage.vue'),
......@@ -72,6 +126,12 @@ const routes = [
}
},
{
path: '/courses/:id/reviews',
name: 'CourseReviews',
component: () => import('../views/courses/CourseReviewsPage.vue'),
meta: { title: '课程评价' }
},
{
path: '/checkout',
name: 'CheckoutPage',
component: () => import('../views/checkout/CheckoutPage.vue'),
......
......@@ -173,7 +173,10 @@
</div>
</div>
<button class="w-full text-center text-green-600 mt-3 text-sm">
<button
@click="router.push(`/courses/${course?.id}/reviews`)"
class="w-full text-center text-green-600 mt-3 text-sm"
>
查看全部评价
</button>
</FrostedGlass>
......
<!--
* @Date: 2025-03-21 11:33:26
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-21 11:33:27
* @FilePath: /mlaj/src/views/courses/CourseReviewsPage.vue
* @Description: 文件描述
-->
<template>
<AppLayout title="全部评价">
<div class="px-4 py-3">
<!-- Overall Rating -->
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="text-2xl font-bold mr-2">4.9</div>
<van-rate v-model="overallRating" readonly allow-half />
</div>
<div class="text-gray-500 text-sm">126条评论</div>
</div>
<!-- Reviews List -->
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<div v-for="(review, index) in reviews" :key="index" class="mb-4 pb-4 border-b border-gray-100 last:border-0">
<div class="flex justify-between items-center mb-2">
<div class="flex items-center flex-1 min-w-0 mr-2">
<div class="flex-grow">
<span class="font-medium text-sm block">{{ review.username }}</span>
</div>
</div>
<van-rate v-model="review.rating" readonly allow-half class="scale-75 -mr-2 flex-shrink-0" />
</div>
<p class="text-gray-600 text-sm mb-2">{{ review.content }}</p>
<div class="text-gray-400 text-xs">{{ review.date }}</div>
</div>
</van-list>
</div>
</AppLayout>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
import { Rate, List } from 'vant'
const route = useRoute()
const overallRating = ref(4.9)
const loading = ref(false)
const finished = ref(false)
const reviews = ref([])
// Mock data for demonstration
const mockReviews = [
{
username: '王小明',
avatar: '',
rating: 5,
content: '课程内容非常实用,老师讲解清晰,帮助我和孩子度过了考前紧张期。',
date: '2024-06-15'
},
{
username: '李晓华',
avatar: '',
rating: 4.5,
content: '老师提供的减压方法很有效,孩子学习状态明显改善,感谢这个课程!',
date: '2024-06-10'
},
// Add more mock reviews here
]
const onLoad = () => {
// Simulate async data loading
setTimeout(() => {
const newReviews = mockReviews.map(review => ({
...review,
avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${Math.random()}`
}))
reviews.value.push(...newReviews)
loading.value = false
// Set finished when no more data
if (reviews.value.length >= 20) {
finished.value = true
}
}, 1000)
}
</script>