hookehuyr

refactor(router): 重构路由配置并删除无用文件

重构了路由配置文件,更新了路由路径和组件名称,并添加了元数据。删除了不再使用的About.vue文件,同时新增了CoursesPage.vue页面。
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-20 20:50:41
* @FilePath: /mlaj/src/router/index.js
* @Description: 文件描述
*/
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'home',
component: () => import('../views/Home.vue')
name: 'HomePage',
component: () => import('../views/HomePage.vue'),
meta: { title: 'HomePage' },
},
{
path: '/about',
name: 'about',
component: () => import('../views/About.vue')
}
path: '/courses',
name: 'Courses',
component: () => import('../views/courses/CoursesPage.vue'),
meta: { title: 'Courses' },
},
{
path: '/courses/:id',
name: 'CourseDetail',
component: () => import('../views/courses/CourseDetailPage.vue'),
meta: { title: 'CourseDetail' },
},
]
const router = createRouter({
history: createWebHistory(),
routes
routes,
})
export default router
......
<script setup>
import { Button, NavBar, Tabbar, TabbarItem } from 'vant';
</script>
<template>
<div class="min-h-screen flex flex-col bg-gray-100">
<van-nav-bar
title="关于"
left-text="返回"
right-text="菜单"
left-arrow
@click-left="onClickLeft"
@click-right="onClickRight"
/>
<div class="flex-1 p-4">
<div class="bg-white rounded-lg shadow p-4 mb-4">
<h2 class="text-xl font-bold mb-2">关于我们</h2>
<p class="text-gray-600 mb-4">这是一个示例项目的关于页面,展示了如何使用Vue3、Vite、Vant4和TailwindCSS构建现代化的Web应用。</p>
<van-button type="primary" block>了解更多</van-button>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold mb-2">联系方式</h3>
<ul class="text-gray-600">
<li>邮箱:example@example.com</li>
<li>电话:123-456-7890</li>
<li>地址:示例地址</li>
</ul>
</div>
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold mb-2">版本信息</h3>
<ul class="text-gray-600">
<li>当前版本:1.0.0</li>
<li>更新日期:2024-03-20</li>
<li>开源协议:MIT</li>
</ul>
</div>
</div>
</div>
<van-tabbar v-model="active">
<van-tabbar-item icon="home-o">首页</van-tabbar-item>
<van-tabbar-item icon="search">搜索</van-tabbar-item>
<van-tabbar-item icon="friends-o">好友</van-tabbar-item>
<van-tabbar-item icon="setting-o">设置</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
data() {
return {
active: 0
}
},
methods: {
onClickLeft() {
this.$router.back()
},
onClickRight() {
// 处理右侧按钮点击
}
}
}
</script>
<!--
* @Date: 2025-03-20 19:55:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-20 20:26:17
* @FilePath: /vue-vite/src/views/Home.vue
* @LastEditTime: 2025-03-20 20:48:08
* @FilePath: /mlaj/src/views/HomePage.vue
* @Description: 文件描述
-->
<template>
......
<template>
<AppLayout title="课程详情" showBackButton :rightContent="rightContent">
<div class="pb-24">
<!-- Course Header -->
<div class="bg-gradient-to-b from-red-500 to-red-600 p-4 mb-4 rounded-b-3xl shadow-lg">
<div class="bg-white/10 backdrop-blur-sm rounded-lg p-3 mb-3 inline-block">
<div class="text-white font-semibold">{{ course?.subtitle?.split(' ')[0] }}</div>
</div>
<h1 class="text-2xl text-white font-bold mb-1">{{ course?.title }}</h1>
<h2 class="text-lg text-white/90">{{ course?.subtitle }}</h2>
<div class="mt-4 flex justify-between items-center">
<div class="text-orange-300 font-bold text-2xl">¥{{ course?.price }}</div>
<div class="bg-orange-500/30 text-orange-100 text-xs px-3 py-1 rounded-full">
限时优惠
</div>
</div>
<div class="flex justify-between text-xs text-white/80 mt-3">
<div>已更新{{ course?.updatedLessons }}期</div>
<div>{{ course?.subscribers }}人订阅</div>
</div>
<div v-if="course?.expireDate" class="text-xs text-white/80 mt-1">
有效期: {{ course?.expireDate }}
</div>
</div>
<!-- Course Main Content -->
<div class="px-4">
<!-- Course Details -->
<FrostedGlass class="mb-4 p-4 rounded-xl">
<h3 class="text-lg font-bold text-gray-800 mb-3">本课程介绍</h3>
<p class="text-gray-700 whitespace-pre-line">
{{ course?.description }}
</p>
</FrostedGlass>
<!-- Course Image -->
<div class="mb-6">
<img
:src="course?.imageUrl"
:alt="course?.title"
class="w-full h-auto rounded-xl shadow-md"
/>
</div>
<!-- Tab Navigation -->
<FrostedGlass class="mb-6 rounded-xl overflow-hidden">
<div class="border-b border-gray-200">
<div class="flex">
<button
v-for="(item, index) in curriculumItems"
:key="index"
@click="activeTab = item.title"
:class="[
'flex-1 py-3 font-medium text-center',
activeTab === item.title
? 'text-green-600 border-b-2 border-green-600 bg-green-50/50'
: 'text-gray-500'
]"
>
{{ item.title }}
</button>
</div>
</div>
<!-- Tab Content -->
<div class="p-4">
<div v-if="activeTab === '课程特色'">
<ul class="list-disc pl-5 space-y-2 text-gray-700">
<li>小班授课,更多关注</li>
<li>名师授课,经验丰富</li>
<li>随堂练习,巩固知识</li>
<li>及时反馈,调整教学</li>
</ul>
</div>
<div v-if="activeTab === '课程大纲'">
<div class="space-y-4">
<div class="border-l-2 border-green-500 pl-3">
<h4 class="font-medium text-gray-800">第一章:心态准备</h4>
<p class="text-sm text-gray-600 mt-1">45分钟 · 3个小节</p>
</div>
<div class="border-l-2 border-gray-300 pl-3">
<h4 class="font-medium text-gray-800">第二章:考前减压</h4>
<p class="text-sm text-gray-600 mt-1">60分钟 · 4个小节</p>
</div>
<div class="border-l-2 border-gray-300 pl-3">
<h4 class="font-medium text-gray-800">第三章:家庭支持</h4>
<p class="text-sm text-gray-600 mt-1">50分钟 · 3个小节</p>
</div>
</div>
</div>
<div v-if="activeTab === '课程亮点'">
<div class="space-y-3 text-gray-700">
<p>✓ 专业的心理辅导</p>
<p>✓ 系统化的学习方法</p>
<p>✓ 实用的家庭沟通技巧</p>
<p>✓ 定制的减压活动推荐</p>
</div>
</div>
<div v-if="activeTab === '学习目标'">
<div class="space-y-3 text-gray-700">
<p>1. 帮助家长理解考前压力来源</p>
<p>2. 掌握有效的情绪管理技巧</p>
<p>3. 建立积极的家庭支持系统</p>
<p>4. 培养考前良好的心态和信心</p>
</div>
</div>
</div>
</FrostedGlass>
<!-- Teacher Introduction -->
<FrostedGlass class="mb-6 p-4 rounded-xl">
<h3 class="text-lg font-bold text-gray-800 mb-3">主讲老师</h3>
<div class="flex items-center">
<div class="w-16 h-16 rounded-full overflow-hidden mr-4">
<img
src="https://cdn.ipadbiz.cn/mlaj/images/teacher-avatar.jpg"
alt="Teacher"
class="w-full h-full object-cover"
@error="handleImageError"
/>
</div>
<div>
<h4 class="font-bold text-gray-900">张明睿</h4>
<p class="text-sm text-gray-600">教育心理学博士</p>
<p class="text-xs text-gray-500 mt-1">10年家庭教育培训经验</p>
</div>
</div>
</FrostedGlass>
<!-- Student Reviews -->
<FrostedGlass class="mb-6 p-4 rounded-xl">
<h3 class="text-lg font-bold text-gray-800 mb-3">学员评价</h3>
<div class="flex items-center mb-3">
<div class="flex text-yellow-400 mr-2">
<svg
v-for="star in 5"
:key="star"
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
/>
</svg>
</div>
<div class="text-gray-700">4.9 (126条评论)</div>
</div>
<div class="space-y-4">
<div class="border-b border-gray-100 pb-3">
<div class="flex justify-between">
<div class="font-medium text-gray-800">王小明</div>
<div class="text-xs text-gray-500">2024-06-15</div>
</div>
<p class="text-sm text-gray-600 mt-1">
课程内容非常实用,老师讲解清晰,帮助我和孩子度过了考前紧张期。
</p>
</div>
<div class="border-b border-gray-100 pb-3">
<div class="flex justify-between">
<div class="font-medium text-gray-800">李晓华</div>
<div class="text-xs text-gray-500">2024-06-10</div>
</div>
<p class="text-sm text-gray-600 mt-1">
老师提供的减压方法很有效,孩子学习状态明显改善,感谢这个课程!
</p>
</div>
</div>
<button class="w-full text-center text-green-600 mt-3 text-sm">
查看全部评价
</button>
</FrostedGlass>
</div>
<!-- Bottom Action Bar -->
<div class="fixed bottom-16 left-0 right-0 bg-white shadow-lg p-3 flex justify-between items-center">
<div class="flex space-x-4">
<button class="flex flex-col items-center text-gray-500 text-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
咨询
</button>
<button class="flex flex-col items-center text-gray-500 text-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
/>
</svg>
分享
</button>
<button class="flex flex-col items-center text-gray-500 text-xs">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
收藏
</button>
</div>
<div class="flex items-center">
<div class="mr-2">
<div class="text-red-500 font-bold">¥{{ course?.price }}</div>
<div class="text-xs text-gray-400 line-through">
¥{{ Math.round(course?.price * 1.2) }}
</div>
</div>
<button class="bg-gradient-to-r from-green-500 to-green-600 text-white px-6 py-2 rounded-full text-sm font-medium shadow-md">
立即购买
</button>
</div>
</div>
</div>
</AppLayout>
</template>
<script setup lang="jsx">
import { ref, onMounted, defineComponent, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import { courses } from '@/utils/mockData'
const route = useRoute()
const router = useRouter()
const course = ref(null)
const activeTab = ref('课程特色')
// Curriculum items
const curriculumItems = [
{ title: '课程特色', active: true },
{ title: '课程大纲', active: false },
{ title: '课程亮点', active: false },
{ title: '学习目标', active: false },
]
// Handle image error
const handleImageError = (e) => {
e.target.src = 'https://via.placeholder.com/150?text=Teacher'
}
// Right content component
const RightContent = defineComponent({
setup() {
return () => (
<button class="p-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
/>
</svg>
</button>
)
},
})
const rightContent = h(RightContent)
// Fetch course data
onMounted(() => {
const id = route.params.id
const foundCourse = courses.find(c => c.id === id)
if (foundCourse) {
course.value = foundCourse
} else {
// Course not found, redirect to courses page
router.push('/courses')
}
})
</script>
<template>
<AppLayout title="课程" :rightContent="rightContent">
<div class="pb-16">
<!-- Search Bar -->
<div class="pb-2">
<SearchBar placeholder="搜索" @search="handleSearch" />
</div>
<!-- Featured Course Banner -->
<div class="px-4 mb-5">
<div class="relative rounded-xl overflow-hidden shadow-lg h-40">
<img
src="https://cdn.ipadbiz.cn/mlaj/images/featured-course.jpg"
alt="传承之道"
class="w-full h-full object-cover"
/>
<div
class="absolute inset-0 bg-gradient-to-b from-transparent to-black/50 flex flex-col justify-end p-4"
>
<h2 class="text-3xl font-bold text-amber-400 drop-shadow-md">传承之道</h2>
<p class="text-xl text-white font-semibold drop-shadow-sm">大理鸡足山游学</p>
</div>
</div>
</div>
<!-- Today's Live -->
<div class="px-4 mb-4">
<div class="flex items-center mb-2">
<h2 class="font-medium">今日直播</h2>
<div
class="ml-2 flex items-center bg-red-100 text-red-700 rounded px-2 py-1 text-xs"
>
<span class="font-medium">{{ hours }}</span>
<span class="mx-1">:</span>
<span class="font-medium">{{ minutes }}</span>
<span class="mx-1">:</span>
<span class="font-medium">00</span>
</div>
</div>
<!-- Live Stream Cards -->
<div class="grid grid-cols-2 gap-4">
<LiveStreamCard
v-for="stream in liveStreams"
:key="stream.id"
:stream="stream"
/>
</div>
</div>
<!-- Value Online Courses -->
<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">
更多
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 ml-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</router-link>
</div>
<!-- Course Cards -->
<div class="space-y-4">
<CourseCard v-for="course in courses" :key="course.id" :course="course" />
</div>
</div>
</div>
</AppLayout>
</template>
<script setup lang="jsx">
import { computed, defineComponent, h } from "vue";
import AppLayout from "@/components/layout/AppLayout.vue";
import SearchBar from "@/components/ui/SearchBar.vue";
import FrostedGlass from "@/components/ui/FrostedGlass.vue";
import CourseCard from "@/components/ui/CourseCard.vue";
import LiveStreamCard from "@/components/ui/LiveStreamCard.vue";
import { featuredCourse, liveStreams, courses } from "@/utils/mockData";
// Search handler
const handleSearch = (query) => {
console.log("Searching for:", query);
// Would implement actual search logic here
};
// Current time for the countdown timer
const todayDate = new Date();
const formattedTime = featuredCourse.liveTime.split(":");
const hours = computed(() => formattedTime[0]);
const minutes = computed(() => formattedTime[1]);
// Right content component
const RightContent = defineComponent({
setup() {
return () => (
<button class="p-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-gray-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
/>
</svg>
</button>
);
},
});
const rightContent = h(RightContent);
</script>