newfront/src/components/CourseCard.vue

326 lines
8.1 KiB
Vue

<template>
<div class="course-card" :class="{ 'loading': isLoading }">
<!-- 标题栏 -->
<div class="course-card-header">相关课程</div>
<div v-if="isLoading" class="loading-spinner">
<div class="spinner"></div>
</div>
<div v-else-if="error" class="error-message">
当前课程似乎不存在呢~
</div>
<div v-else-if="course" class="course-content">
<!-- 标题和标签行 -->
<div class="course-header">
<h3 class="course-title">{{ course.course_name }}</h3>
<div class="course-tags">
<span
v-for="(tag, index) in course.titles"
:key="index"
class="course-tag"
:style="{ backgroundColor: tag.color }"
>
{{ tag.title }}
</span>
</div>
</div>
<!-- 课程信息 -->
<div class="course-info">
<div class="course-category">{{ getCategoryName(course.category_id) }}</div>
<div class="course-teachers">{{ course.teachers }}</div>
</div>
<!-- 评分区域 -->
<div class="course-rating-container">
<div class="course-rating">
<span class="rating-number">{{ course.rating }}</span>
<div class="rating-stars">
<span class="stars" :style="{ width: (parseFloat(course.rating) / 5 * 100) + '%' }"></span>
</div>
<span class="rating-count">({{ course.rating_count }}人评)</span>
</div>
</div>
<!-- 详情按钮 -->
<div class="course-actions">
<button class="view-detail-button" @click="viewCourseDetail">查看详情</button>
</div>
</div>
</div>
</template>
<script>
import messageBox from '@/utils/messageBox.js'
export default {
name: 'CourseCard',
props: {
courseId: {
type: [Number, String],
required: true
}
},
data() {
return {
course: null,
isLoading: true,
error: null,
categoryMap: {
1: '通识选修类',
2: '人文选修类',
3: '专业方向类',
4: '体育类',
5: '学科基础类',
6: '暑期国际课',
7: '数学与自然科学类',
8: '重修专栏',
9: '数学与自然科学类(必修)',
10: '人文社会科学类(必修)',
11: '学科基础类(必修)',
12: '专业方向类(必修)',
13: '实践类(必修)'
}
}
},
mounted() {
this.fetchCourseDetails();
},
methods: {
async fetchCourseDetails() {
this.isLoading = true;
this.error = null;
try {
const response = await fetch(`https://coursesystem.xn--xhq44jb2fzpc.com/course-detail?course_id=${this.courseId}`);
if (!response.ok) {
throw new Error('获取课程信息失败');
}
this.course = await response.json();
} catch (err) {
this.error = err.message;
console.error('获取课程详情出错:', err);
} finally {
this.isLoading = false;
}
},
getCategoryName(categoryId) {
return this.categoryMap[categoryId] || '未知类别';
},
viewCourseDetail() {
// 检测是否为移动设备
// const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth < 768;
const isMobile = false;
if (isMobile) {
// 移动设备显示确认对话框
messageBox.confirm("建议在电脑端查看课程评分系统以获得最佳体验。仍要继续吗?", "提示")
.then(() => {
// 用户确认,跳转到投稿页面
// 将课程ID转为字符串并进行base64编码
// const courseIdBase64 = btoa(String(this.courseId));
// 构建URL并在新窗口打开
const url = `https://course.东北大学.com/detail/${this.courseId}`;
window.open(url, '_blank');
})
.catch(() => {
// 用户取消,不执行任何操作
console.log("用户取消了在移动端查看课程评分系统");
});
} else {
// 将课程ID转为字符串并进行base64编码
// const courseIdBase64 = btoa(String(this.courseId));
// 构建URL并在新窗口打开
const url = `https://course.东北大学.com/detail/${this.courseId}`;
window.open(url, '_blank');
}
}
}
}
</script>
<style scoped lang="scss">
.course-card {
width: 100%;
max-width: 400px;
min-height: 180px;
border-radius: 8px;
background: #ffffff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
padding: 16px;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
position: relative;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
}
&.loading {
display: flex;
justify-content: center;
align-items: center;
}
}
.course-card-header {
font-size: 0.85rem;
color: #666;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px dashed #eaeaea;
text-align: left;
font-weight: 500;
}
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
min-height: 180px;
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3273dc;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
}
.error-message {
color: #e74c3c;
text-align: center;
padding: 20px;
font-size: 1rem;
}
.course-content {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.course-header {
display: flex;
flex-direction: column;
margin-bottom: 12px;
}
.course-title {
font-size: 1.25rem;
font-weight: 600;
color: #333;
margin: 0 0 8px 0;
line-height: 1.4;
margin-top: 0.5rem !important; /* 添加顶部间距 */
margin-bottom: 0.5rem !important; /* 添加底部间距 */
}
.course-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
// margin-bottom: 8px;
.course-tag {
font-size: 0.75rem;
padding: 2px 8px;
border-radius: 4px;
color: white;
font-weight: 500;
}
}
.course-info {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
color: #666;
font-size: 0.9rem;
}
.course-category {
padding: 2px 8px;
background-color: #f3f4f6;
border-radius: 4px;
font-size: 0.8rem;
}
.course-teachers {
// font-style: italic;
}
.course-rating-container {
margin-top: auto;
padding-top: 12px;
border-top: 1px solid #eee;
margin-bottom: 16px;
}
.course-rating {
display: flex;
align-items: center;
.rating-number {
font-size: 1.5rem;
font-weight: 700;
color: #ff9800;
margin-right: 8px;
}
.rating-stars {
position: relative;
display: inline-block;
width: 80px;
height: 16px;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSIjZTBlMGUwIj48cGF0aCBkPSJNMTIgMTcuMjdsNi4xOCAzLjczLTEuNjQtNy4wM0wyMiA5LjI0bC03LjE5LS42MUwxMiAyIDkuMTkgOC42MyAyIDkuMjRsNS40NiA0LjczLTEuNjQgNy4wM3oiLz48L3N2Zz4=') repeat-x;
background-size: 16px 16px;
.stars {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSIjZmZhMDAwIj48cGF0aCBkPSJNMTIgMTcuMjdsNi4xOCAzLjczLTEuNjQtNy4wM0wyMiA5LjI0bC03LjE5LS42MUwxMiAyIDkuMTkgOC42MyAyIDkuMjRsNS40NiA0LjczLTEuNjQgNy4wM3oiLz48L3N2Zz4=') repeat-x;
background-size: 16px 16px;
}
}
.rating-count {
font-size: 0.8rem;
color: #888;
margin-left: 8px;
}
}
.course-actions {
display: flex;
justify-content: center;
.view-detail-button {
background-color: #3273dc;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
&:hover {
background-color: #2366c9;
}
}
}
</style>