fix:修复报错

This commit is contained in:
yuk255 2025-10-09 14:49:41 +08:00
parent 5b125c15b2
commit 68f3652935
2 changed files with 1019 additions and 0 deletions

View File

@ -0,0 +1,521 @@
<template>
<div class="activity-content">
<!-- 活动筛选标签 -->
<div class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activeActivityTab === 'all' }"
@click="handleActivityTabChange('all')">全部活动</span>
<span class="text_13" :class="{ active: activeActivityTab === 'ongoing' }"
@click="handleActivityTabChange('ongoing')">进行中</span>
<span class="text_14" :class="{ active: activeActivityTab === 'finished' }"
@click="handleActivityTabChange('finished')">已结束</span>
</div>
<!-- 活动分割线 -->
<div class="course-divider"></div>
<!-- 活动网格 -->
<div class="activity-grid">
<div v-for="activity in filteredActivities" :key="activity.id" class="activity-card">
<!-- 活动卡片顶部图片 -->
<div class="activity-card-image">
<img :src="activity.banner || activity.imgs || '/banners/banner1.png'" alt="活动图片" class="activity-image" />
</div>
<!-- 活动标题 -->
<h3 class="activity-title">{{ activity.title }}</h3>
<!-- 活动简介 -->
<div class="activity-intro">
<!-- <div class="intro-title">活动简介</div> -->
<div class="intro-content" v-html="activity.introduction">
</div>
</div>
<!-- 活动详情 -->
<div class="activity-details">
<div class="activity-meta-item">
<span class="meta-label">开始时间</span>
<span class="meta-value">{{ activity.startTime }}</span>
</div>
<div class="activity-meta-item">
<span class="meta-label">结束时间</span>
<span class="meta-value">{{ activity.endTime }}</span>
</div>
<div class="activity-meta-item">
<span class="meta-label">最大人数</span>
<span class="meta-value">{{ activity.maxNum }}</span>
</div>
</div>
<!-- 活动底部 -->
<div class="activity-footer">
<div class="activity-action-right">
<!-- <button v-if="activity.status === '1'" class="action-btn ongoing-btn"
@click="continueActivity(activity.id)">
查看活动
</button> -->
<button
class="action-btn ongoing-btn" @click="viewActivityDetail(activity.id)">
查看详情
</button>
</div>
<div class="activity-status-left">
<span :class="['activity-status-text', activity.status === '1' ? 'ongoing' : 'finished']">
{{ activity.status_dictText }}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useMessage } from 'naive-ui'
import { useRouter } from 'vue-router'
import { ActivityListApi } from '@/api/modules/userCenter'
const message = useMessage()
const router = useRouter()
//
interface Activity {
id: string
title: string
introduction: string
imgs: string
banner: string
video: string | null
maxNum: number
startTime: string
endTime: string
extra: string | null
attachment: string
status: string
createBy: string
createTime: string
updateBy: string
updateTime: string
status_dictText: string
}
//
const activeActivityTab = ref('all')
//
const activities = ref<Activity[]>([])
//
const filteredActivities = computed(() => {
if (activeActivityTab.value === 'ongoing') {
return activities.value.filter(activity => activity.status === '1')
} else if (activeActivityTab.value === 'finished') {
return activities.value.filter(activity => activity.status !== '1')
}
return activities.value
})
//
const handleActivityTabChange = (tab: string) => {
activeActivityTab.value = tab
}
//
// const continueActivity = (id: string) => {
// console.log(':', id)
// message.info(` ${id}`)
// }
//
const viewActivityDetail = (id: string) => {
router.push(`/activity/${id}`)
}
onMounted(async () => {
try {
const response = await ActivityListApi.getActivityList()
console.log('活动列表:', response.data.result)
activities.value = response.data.result.records || []
} catch (error) {
message.error('请求活动列表时发生错误')
console.error(error)
}
})
</script>
<style scoped>
/* 活动页面样式 - 网格布局 */
.activity-content {
width: 100%;
}
/* 课程筛选标签 */
.text-wrapper_1 {
width: 100%;
height: 2.08vh;
align-items: center;
margin: 20px 0 20px 0;
gap: 2.81vw;
}
.text_12,
.text_13,
.text_14 {
font-size: 0.94vw;
color: #000;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
cursor: pointer;
padding: 0.42vh 0;
transition: color 0.3s ease;
background: transparent;
}
.text_12.active,
.text_13.active,
.text_14.active {
color: rgba(2, 134, 206, 1);
}
.text_12:hover,
.text_13:hover,
.text_14:hover {
color: rgba(2, 134, 206, 1);
}
/* 课程区域分割线 */
.course-divider {
width: 100%;
height: 1.5px;
background: #E6E6E6;
margin-bottom: 1.67vh;
}
.activity-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.04vw;
margin-top: 1.04vh;
}
.activity-card {
background: #ffffff;
border: 1px solid #e8e8e8;
padding: 0;
position: relative;
min-height: 14.58vh;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
overflow: hidden;
}
.activity-card:hover {
box-shadow: 0 0.21vw 0.63vw rgba(0, 0, 0, 0.1);
transform: translateY(-0.1vh);
}
/* 活动卡片顶部图片容器 */
.activity-card-image {
width: 100%;
height: 179px;
overflow: hidden;
}
/* 活动卡片图片 */
.activity-image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.activity-score-badge {
position: absolute;
top: 0.83vw;
right: 0.83vw;
background: #FF6B35;
color: white;
padding: 0.21vw 0.42vw;
border-radius: 0.21vw;
font-size: 0.63vw;
font-weight: 500;
z-index: 1;
}
.activity-title {
font-size: 16px;
color: #333;
margin: 15px 0 5px 0;
line-height: 1.4;
padding: 0 1.04vw;
font-weight: 600;
}
.activity-details {
padding: 0 1.04vw;
}
.activity-meta-item {
margin-bottom: 10px;
font-size: 14px;
line-height: 1.4;
display: flex;
}
.meta-label {
color: #999;
min-width: 70px;
}
.meta-value {
color: #333;
font-weight: 500;
}
.activity-intro {
flex: 1;
margin-bottom: 1.04vh;
padding: 0 1.04vw;
}
.intro-title {
font-size: 0.73vw;
font-weight: 600;
color: #333;
margin-bottom: 0.42vh;
}
.intro-content {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 10px;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.activity-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
padding: 5px 15px 15px 15px;
flex-shrink: 0;
}
.activity-status-left {
display: flex;
align-items: center;
}
.activity-status-text {
font-size: 12px;
font-weight: 500;
}
/* 活动状态颜色 - 与练习中保持一致 */
.activity-status-text.ongoing {
color: #FF520F;
}
.activity-status-text.finished {
color: #999999;
}
.activity-action-right {
display: flex;
gap: 10px;
}
.action-btn {
padding: 6px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border: none;
transition: all 0.3s ease;
min-width: 80px;
}
.ongoing-btn {
background: #0288D1;
color: white;
}
.ongoing-btn:hover {
background: #40a9ff;
}
.finished-btn {
background: #0288D1;
color: white;
}
.finished-btn:hover {
background: #01579B;
}
/* 活动已结束按钮样式 - 灰色 */
.activity-finished-btn {
background-color: #999999 !important;
border-color: #999999 !important;
color: #ffffff !important;
}
.activity-finished-btn:hover {
background-color: #808080 !important;
border-color: #808080 !important;
}
/* 通用样式 */
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
}
/* 活动响应式设计 */
@media (max-width: 1200px) {
.activity-grid {
grid-template-columns: repeat(2, 1fr);
}
.activity-score-badge {
font-size: 1vw;
padding: 0.3vw 0.6vw;
}
.activity-title {
font-size: 1.2vw;
}
.activity-meta-item {
font-size: 1vw;
}
.intro-title {
font-size: 1.1vw;
}
.intro-content {
font-size: 1vw;
}
.activity-status-text {
font-size: 1vw;
}
}
@media (max-width: 768px) {
.activity-grid {
grid-template-columns: 1fr;
gap: 2vw;
}
.activity-card {
padding: 0;
min-height: 30vh;
}
/* 手机端活动卡片图片 */
.activity-card-image {
height: 12vh;
}
/* 手机端活动内容区域添加内边距 */
.activity-title,
.activity-details,
.activity-intro {
padding-left: 3vw;
padding-right: 3vw;
}
.activity-footer {
padding: 0 3vw 3vw;
}
.activity-score-badge {
font-size: 2.5vw;
padding: 1vw 2vw;
top: 2vw;
right: 2vw;
}
.activity-title {
font-size: 3.5vw;
margin-bottom: 2vh;
}
.activity-meta-item {
font-size: 2.8vw;
margin-bottom: 1vh;
}
.intro-title {
font-size: 3vw;
margin-bottom: 1vh;
}
.intro-content {
font-size: 2.8vw;
margin-bottom: 2vh;
}
.activity-status-text {
font-size: 2.8vw;
}
.text-wrapper_1 {
justify-content: flex-start;
gap: 4vw;
flex-wrap: wrap;
}
.text_12,
.text_13,
.text_14 {
font-size: 2.16px;
padding: 10px 0;
}
.course-divider {
width: 100%;
}
}
/* 更小屏幕的适配 */
@media (max-width: 480px) {
.activity-card {
padding: 0;
min-height: 220px;
}
.activity-title {
font-size: 14px;
}
.activity-score-badge {
top: 12px;
right: 12px;
}
}
@media (max-width: 576px) {
.text-wrapper_1 {
gap: 0.83vw;
}
.text_12,
.text_13,
.text_14 {
font-size: 0.83vw;
padding: 0.31vh 0.63vw;
}
}
</style>

View File

@ -0,0 +1,498 @@
<template>
<div class="practice-content">
<!-- 练习筛选标签 -->
<div class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activePracticeTab === 'all' }"
@click="handlePracticeTabChange('all')">全部练习</span>
<span class="text_13" :class="{ active: activePracticeTab === 'ongoing' }"
@click="handlePracticeTabChange('ongoing')">练习中</span>
<span class="text_14" :class="{ active: activePracticeTab === 'finished' }"
@click="handlePracticeTabChange('finished')">已结束</span>
</div>
<!-- 练习分割线 -->
<div class="course-divider"></div>
<!-- 练习网格 -->
<div class="exam-grid">
<div v-for="practice in filteredPractices" :key="practice.id" class="exam-card">
<!-- 考试标题 -->
<div class="exam-title">{{ practice.title }}</div>
<!-- 分数显示 -->
<div v-if="practice.status === 'finished'" class="practice-score-badge">
<span class="score-text">{{ practice.score }}<span></span></span>
</div>
<!-- 考试信息 -->
<div class="exam-details">
<div class="exam-meta-item">
<span class="meta-label">练习日期</span>
<span class="meta-value">{{ practice.practiceDate }}</span>
</div>
<div class="exam-meta-item">
<span class="meta-label">练习时间</span>
<span class="meta-value">{{ practice.duration }}分钟</span>
</div>
<div class="exam-meta-item">
<span class="meta-label">题库数量</span>
<span class="meta-value">{{ practice.questionCount }}</span>
</div>
</div>
<!-- 动态显示内容 -->
<div v-if="practice.status === 'finished'" class="practice-stats">
<div class="stat-row">
<span class="stat-label">答对</span>
<span class="stat-value correct">{{ practice.correctCount || 0 }}<span></span></span>
</div>
<!-- 分割线 -->
<div class="divider"></div>
<div class="stat-row">
<span class="stat-label">答错</span>
<span class="stat-value wrong">{{ practice.wrongCount || 0 }}<span></span></span>
</div>
</div>
<div v-else class="exam-description">
{{ practice.description || '该练习包含' + practice.questionCount + '道题目,涵盖了相关知识点。' }}
</div>
<!-- 底部操作区域 -->
<div class="exam-footer">
<div class="exam-action-right">
<button v-show="practice.status === 'ongoing'" class="action-btn ongoing-btn"
@click="continuePractice(practice.id)">
继续练习
</button>
<button v-show="practice.status === 'finished'" class="action-btn finished-btn"
@click="viewPracticeDetail(practice.id)">
查看详情
</button>
</div>
<div class="exam-status-left">
<span class="exam-status-text">{{ practice.status === 'ongoing' ? '练习中' : '已结束' }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useMessage } from 'naive-ui'
import { useRouter } from 'vue-router'
const message = useMessage()
const router = useRouter()
//
interface Practice {
id: number
title: string
practiceDate: string
duration: number
questionCount: number
description: string
status: 'ongoing' | 'finished'
score: number | null
correctCount: number
wrongCount: number
}
//
const activePracticeTab = ref('all')
//
const mockPractices: Practice[] = [
{
id: 1,
title: 'C++语言程序设计基础练习题',
practiceDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'ongoing',
score: null,
correctCount: 18,
wrongCount: 15
},
{
id: 2,
title: 'C++语言程序设计基础练习题',
practiceDate: '2025-07-18 10:00',
duration: 88,
questionCount: 100,
description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'finished',
score: 75,
correctCount: 18,
wrongCount: 12
},
{
id: 3,
title: 'Java程序设计高级练习',
practiceDate: '2025-07-15 14:30',
duration: 90,
questionCount: 80,
description: '深入Java高级特性包括并发编程、集合框架、网络编程等内容的综合练习。',
status: 'finished',
score: 88,
correctCount: 70,
wrongCount: 10
}
]
//
const filteredPractices = computed(() => {
if (activePracticeTab.value === 'ongoing') {
return mockPractices.filter(practice => practice.status === 'ongoing')
} else if (activePracticeTab.value === 'finished') {
return mockPractices.filter(practice => practice.status === 'finished')
}
return mockPractices
})
//
const handlePracticeTabChange = (tab: string) => {
activePracticeTab.value = tab
}
//
const continuePractice = (practiceId: number) => {
message.info(`继续练习 ${practiceId}`)
}
//
const viewPracticeDetail = (practiceId: number) => {
router.push(`/exam-detail/${practiceId}?source=practice`)
}
</script>
<style scoped>
/* 练习页面样式 - 网格布局 */
.practice-content {
width: 100%;
}
/* 课程筛选标签 */
.text-wrapper_1 {
width: 100%;
height: 2.08vh;
align-items: center;
margin: 20px 0 20px 0;
gap: 2.81vw;
}
.text_12,
.text_13,
.text_14 {
font-size: 0.94vw;
color: #000;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
cursor: pointer;
padding: 0.42vh 0;
transition: color 0.3s ease;
background: transparent;
}
.text_12.active,
.text_13.active,
.text_14.active {
color: rgba(2, 134, 206, 1);
}
.text_12:hover,
.text_13:hover,
.text_14:hover {
color: rgba(2, 134, 206, 1);
}
/* 课程区域分割线 */
.course-divider {
width: 100%;
height: 1.5px;
background: #E6E6E6;
margin-bottom: 1.67vh;
}
.exam-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 20px;
}
.exam-card {
background: #ffffff;
border: 1.5px solid #D8D8D8;
padding: 20px;
position: relative;
min-height: 280px;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
.exam-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.exam-title {
font-size: 16px;
color: #333;
margin-bottom: 10px;
line-height: 1.4;
border-bottom: 1.5px solid #E6E6E6;
padding-bottom: 10px;
}
.practice-score-badge {
position: absolute;
top: 11px;
right: 18px;
background: white;
border: 1px solid #FF6F0F;
border-radius: 4px;
padding: 0 11px;
}
.score-text {
font-size: 20px;
font-weight: bold;
color: #FF6F0F;
}
.score-text span {
margin-left: 2px;
font-size: 10px;
}
.exam-details {
margin-bottom: 5px;
}
.exam-meta-item {
display: flex;
margin-bottom: 8px;
font-size: 14px;
}
.meta-label {
color: #999;
min-width: 70px;
}
.meta-value {
color: #333;
font-weight: 500;
}
.exam-description {
font-size: 13px;
color: #666;
line-height: 1.5;
margin-bottom: 16px;
flex: 1;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
background-color: #F5F8FB;
}
.practice-stats {
display: flex;
gap: 16px;
height: 54px;
background: #F5F8FB;
align-items: center;
justify-content: space-evenly;
}
.divider {
width: 1px;
height: 20px;
background: #EBEBEB;
}
.stat-row {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.stat-label {
font-size: 10px;
color: #497087;
}
.stat-value {
margin-left: 20px;
color: #333;
font-weight: 500;
font-size: 18px;
}
.stat-value span {
margin-left: 2px;
font-size: 10px;
}
.stat-value.correct {
color: #3F76ED;
}
.stat-value.wrong {
color: #FE2E2F;
}
.exam-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
padding-top: 16px;
flex-shrink: 0;
}
.exam-status-left {
display: flex;
align-items: center;
}
.exam-status-text {
font-size: 12px;
color: #999;
}
.exam-action-right {
display: flex;
gap: 10px;
}
.action-btn {
padding: 6px 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border: none;
transition: all 0.3s ease;
min-width: 80px;
}
.ongoing-btn {
background: #0288D1;
color: white;
}
.ongoing-btn:hover {
background: #40a9ff;
}
.finished-btn {
background: #0288D1;
color: white;
}
.finished-btn:hover {
background: #01579B;
}
/* 通用样式 */
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.exam-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.exam-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.exam-card {
padding: 16px;
min-height: 240px;
}
/* 考试标题在小屏幕上给分数留出空间并允许换行 */
.exam-title {
padding-right: 80px;
word-wrap: break-word;
word-break: break-word;
white-space: normal;
}
/* 分数徽章保持在右上角不换行 */
.practice-score-badge {
position: absolute;
top: 16px;
right: 16px;
white-space: nowrap;
}
.text-wrapper_1 {
justify-content: flex-start;
gap: 4vw;
flex-wrap: wrap;
}
.text_12,
.text_13,
.text_14 {
font-size: 2.16px;
padding: 10px 0;
}
.course-divider {
width: 100%;
}
}
/* 更小屏幕的适配 */
@media (max-width: 480px) {
.exam-card {
padding: 12px;
min-height: 220px;
}
.exam-title {
padding-right: 70px;
font-size: 14px;
}
.practice-score-badge {
top: 12px;
right: 12px;
}
.score-text {
font-size: 16px;
}
}
@media (max-width: 576px) {
.text-wrapper_1 {
gap: 0.83vw;
}
.text_12,
.text_13,
.text_14 {
font-size: 0.83vw;
padding: 0.31vh 0.63vw;
}
}
</style>