8739 lines
211 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="profile-page">
<!-- 刷新遮罩 -->
<div v-if="isRefreshing" class="refresh-mask">
<div class="refresh-content">
<div class="refresh-spinner"></div>
<p>正在刷新...</p>
</div>
</div>
<!-- 主要内容区域 -->
<div class="profile-content flex-row">
<!-- 左侧侧边栏 -->
<div class="block_14">
<!-- 用户头像和姓名 -->
<SafeAvatar class="image_7" :src="userStore.user?.avatar"
:name="userStore.user?.profile?.realName || userStore.user?.nickname || userStore.user?.username || '用户'"
:size="96" alt="用户头像" />
<span class="text_72">{{ userStore.user?.profile?.realName || userStore.user?.nickname ||
userStore.user?.username || '用户名' }}</span>
<!-- 菜单项容器 -->
<div class="box_22">
<!-- 分割线 -->
<div class="menu-divider"></div>
<!-- 动态菜单 -->
<div
v-for="(menu, index) in visibleMenuItems"
:key="menu.id"
:class="[`menu-item-${index}`, { active: activeTab === getMenuTabKey(menu.path) }]"
@click="handleMenuSelect(getMenuTabKey(menu.path))"
>
<img
class="menu-icon default-icon"
referrerpolicy="no-referrer"
:src="menu.icon"
:alt="`${menu.name}图标`"
/>
<img
class="menu-icon hover-icon"
referrerpolicy="no-referrer"
:src="getActiveIcon(menu.icon)"
:alt="`${menu.name}激活图标`"
/>
<span class="menu-text">{{ menu.name }}</span>
</div>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="group_5 flex-col">
<!-- 课程内容 -->
<!-- <div>
<div class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activeCourseTab === 'all' }"
@click="handleCourseTabChange('all')">全部课程</span>
<span class="text_13" :class="{ active: activeCourseTab === 'learning' }"
@click="handleCourseTabChange('learning')">学习中</span>
<span class="text_14" :class="{ active: activeCourseTab === 'completed' }"
@click="handleCourseTabChange('completed')">已完结</span>
</div>
<div class="course-divider"></div>
<div class="course-list">
<div v-for="course in filteredCourses" :key="course.id" class="box_2 flex-row justify-between">
<div class="block_4 flex-col">
<div class="box_3 flex-row justify-between">
<div class="status-image-container">
<img class="status-image" referrerpolicy="no-referrer"
:src="course.status === 'learning' ? '/images/icon/learning.png' : '/images/icon/finish.png'"
alt="{{ getStatusText(course.status) }}" />
</div>
<img class="thumbnail_4" referrerpolicy="no-referrer" :src="bannerImage" :alt="bannerAlt" />
<span :class="['status-text', course.status]">{{ getStatusText(course.status) }}</span>
</div>
</div>
<div class="block_5 flex-col">
<div class="group_6 flex-row">
<span class="text_16">{{ course.title }}</span>
<img class="thumbnail_5" referrerpolicy="no-referrer"
src="https://lanhu-oss-2537-2.lanhuapp.com/SketchPnge131c44858bb817d4885b81fdd8ddff8507f62cd51f1025cee826849e6a52c65" />
<span class="text_17">{{ course.rating || 541 }}</span>
</div>
<span class="text_18">讲师{{ course.instructor }}</span>
<span class="text_19">{{ course.description }}</span>
<div class="group_7 flex-row">
<img class="thumbnail_6" referrerpolicy="no-referrer" src="/images/profile/11.png" />
<span class="text_20">{{ course.chapters || 9 }}{{ course.lessons || 54 }}</span>
<img class="thumbnail_7" referrerpolicy="no-referrer" src="/images/profile/22.png" />
<span class="text_21">{{ course.duration || '12小时43分钟' }}</span>
<img class="thumbnail_8" referrerpolicy="no-referrer"
:src="course.status === 'learning' ? '/images/profile/33.png' : '/images/profile/33.png'" />
<span class="text_22">已看{{ course.watchedTime || '10小时20分钟' }}</span>
<div class="text-wrapper_2 flex-col" @click="goToCourse(course.id)">
<span class="text_23">{{ course.status === 'learning' ? '去学习' : '去复习' }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="pagination-wrapper" v-if="totalPages > 1">
<div class="pagination">
<span class="pagination-item nav-button" :class="{ disabled: currentPage === 1 }"
@click="goToPage('first')">
首页
</span>
<span class="pagination-item nav-button" :class="{ disabled: currentPage === 1 }"
@click="goToPage('prev')">
上一页
</span>
<span v-for="page in totalPages" :key="page" class="pagination-item page-number"
:class="{ active: page === currentPage }" @click="goToPage(page)">
{{ page }}
</span>
<span class="pagination-item nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('next')">
下一页
</span>
<span class="pagination-item nav-button" :class="{ disabled: currentPage === totalPages }"
@click="goToPage('last')">
尾页
</span>
</div>
</div>
</div> -->
<CourseContent v-if="isCoursesTab"></CourseContent>
<!-- 作业内容 -->
<div v-else-if="isHomeworkTab" class="homework-content">
<!-- 作业筛选标签 -->
<div v-if="!showDraftBoxView" class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activeHomeworkTab === 'all' }"
@click="handleHomeworkTabChange('all')">全部作业</span>
<span class="text_13" :class="{ active: activeHomeworkTab === 'pending' }"
@click="handleHomeworkTabChange('pending')">未完成</span>
<span class="text_14" :class="{ active: activeHomeworkTab === 'completed' }"
@click="handleHomeworkTabChange('completed')">已完成</span>
<span class="draftbox" @click="showDraftBox">
<img src="/images/auth/mti.png" alt="">
<span>草稿箱</span>
</span>
</div>
<!-- 分割线 -->
<div v-if="!showDraftBoxView" class="course-divider"></div>
<!-- 面包屑 -->
<!-- <div class="breadcrumb-wrapper flex-row">
<span class="text_15">全部作业</span>
<span class="text_15">></span>
<span class="text_15 homework">作业名称</span>
</div> -->
<!-- 作业详情视图 -->
<div v-if="showDetailView && detailAssignment">
<!-- <div class="detail-header">
<div class="breadcrumb-nav">
<span class="breadcrumb-item" @click="backToAssignmentList">全部作业</span>
<span class="breadcrumb-separator">></span>
<span class="breadcrumb-current">作业名称</span>
</div>
</div> -->
<div class="group_11">
<!-- 头部信息 -->
<div class="box_5">
<div class="image-text_2">
<div class="avatar-line"></div>
<img :src="detailAssignment.teacherAvatar" alt="教师头像" class="image_22" />
<div class="text-group_3">
<span class="text_30">{{ detailAssignment.teacherName }}</span>
<span class="text_31">{{ detailAssignment.assignTime }}</span>
</div>
</div>
<span class="text_32">
<img v-if="detailAssignment.status === '已提交'" src="/images/profile/55.png" alt="">
<span :style="{
color: detailAssignment.status === '未完成' || detailAssignment.status === '待提交' ? '#FF560C' : '#999999',
fontSize: '14px'
}">
{{ detailAssignment.status === '未完成' || detailAssignment.status === '待提交' ? '未完成' :
(detailAssignment.status === '已完成' ? '已完成' : '541人已完成') }}
</span>
</span>
</div>
<!-- 作业内容 -->
<div class="text-group_4">
<div class="course-divider"></div>
<span class="text_33">{{ detailAssignment.title }}</span>
<div class="description-container">
<span class="text_34 description-full-view">
{{ detailAssignment.description }}
</span>
</div>
</div>
<!-- 作业附件区域 -->
<div class="box_6">
<div class="attachment-images">
<img v-for="(_, index) in detailAssignment.attachments.slice(0, 5)" :key="index"
src="/images/traings/traing1.png" :class="['image_' + (24 + index)]" />
</div>
</div>
<div class="files-container">
<div class="file-items">
<img src="/images/auth/file.png" alt="" class="files-icon">
<span>文件名称.PDF</span>
<img src="/images/auth/download.png" alt="" class="files-icon">
</div>
<div class="file-items">
<img src="/images/auth/file.png" alt="" class="files-icon">
<span>文件名称.PDF</span>
<img src="/images/auth/download.png" alt="" class="files-icon">
</div>
</div>
<div class="course-name">这里是课程名称!!! <span>查看详情></span></div>
<!-- 作业按钮区域 -->
<div class="assignment-buttons">
<template v-if="detailAssignment.status === '未完成' || detailAssignment.status === '待提交'">
<div class="text-wrapper_8 submit-button" @click="showUploadFromDetail">
<span class="">上传作业</span>
</div>
</template>
</div>
</div>
</div>
<!-- 草稿箱视图 -->
<div v-if="showDraftBoxView && draftAssignment">
<div class="detail-header">
<div class="breadcrumb-nav">
<span class="breadcrumb-item" @click="backFromDraftBox">全部作业</span>
<span class="breadcrumb-separator">></span>
<span class="breadcrumb-current">草稿箱</span>
</div>
</div>
<div class="group_11">
<!-- 头部信息 -->
<div class="box_5">
<div class="image-text_2">
<div class="avatar-line"></div>
<img :src="draftAssignment.teacherAvatar" alt="教师头像" class="image_22" />
<div class="text-group_3">
<span class="text_30">{{ draftAssignment.teacherName }}</span>
<span class="text_31">{{ draftAssignment.assignTime }}</span>
</div>
</div>
<span class="text_32">
<img v-if="draftAssignment.status === '已提交'" src="/images/profile/55.png" alt="">
<span :style="{
color: draftAssignment.status === '未完成' || draftAssignment.status === '待提交' ? '#FF560C' : '#999999',
fontSize: '14px'
}">
{{ draftAssignment.status === '未完成' || draftAssignment.status === '待提交' ? '未完成' :
(draftAssignment.status === '已完成' ? '已完成' : '541人已完成') }}
</span>
</span>
</div>
<!-- 作业内容 -->
<div class="text-group_4">
<span class="text_33">{{ draftAssignment.title }}</span>
<div class="description-container">
<span class="text_34 description-full-view">
{{ draftAssignment.description }}
</span>
</div>
</div>
<!-- 作业附件区域 -->
<div class="box_6">
<div class="attachment-images">
<img v-for="(_, index) in draftAssignment.attachments.slice(0, 5)" :key="index"
src="/images/traings/traing1.png" :class="['image_' + (24 + index)]" />
</div>
</div>
<div class="files-container">
<div class="file-items">
<img src="/images/auth/file.png" alt="" class="files-icon">
<span>文件名称.PDF</span>
<img src="/images/auth/download.png" alt="" class="files-icon">
</div>
<div class="file-items">
<img src="/images/auth/file.png" alt="" class="files-icon">
<span>文件名称.PDF</span>
<img src="/images/auth/download.png" alt="" class="files-icon">
</div>
</div>
<div class="course-name">这里是课程名称!!! <span>查看详情></span></div>
<!-- 作业按钮区域 -->
<div class="assignment-buttons">
<div class="text-wrapper_8 submit-button" @click="showUploadModal(draftAssignment)">
<span class="text_36">上传作业</span>
</div>
<div class="text-wrapper_8 anew-button" @click="reEditDraft">
<span class="text_36">重新编辑</span>
</div>
</div>
</div>
</div>
<!-- 作业列表 -->
<div v-else-if="!showDetailView && !showDraftBoxView">
<div v-for="assignment in filteredAssignments" :key="assignment.id" class="group_11">
<!-- 作业头部信息 -->
<div class="box_5">
<div class="image-text_2">
<div class="avatar-line"></div>
<img src="/images/traings/traing1.png" class="image_22" />
<div class="text-group_3">
<span class="text_30">{{ assignment.teacherName }}</span>
<span class="text_31">{{ assignment.assignTime }}</span>
</div>
</div>
<span class="text_32">
<span :style="{
color: assignment.status === '未完成' || assignment.status === '待提交' ? '#FF560C' : '#999999',
fontSize: '14px'
}">
{{ assignment.status === '未完成' || assignment.status === '待提交' ? '未完成' : '已完成' }}
</span>
</span>
</div>
<!-- 作业内容 -->
<div class="text-group_4">
<!-- 分割线 -->
<div class="course-divider"></div>
<span class="text_33">{{ assignment.title }}</span>
<div class="description-container">
<span class="text_34 text-truncated">
{{ assignment.description }}
</span>
</div>
</div>
<!-- 作业附件区域 -->
<div class="box_6">
<!-- 附件图片列表 -->
<div class="attachment-images">
<img v-for="(_, index) in assignment.attachments.slice(0, 5)" :key="index"
src="/images/traings/traing1.png" :class="['image_' + (24 + index)]" />
</div>
<div class="attachment-images attachment-number-container">
<img src="/images/profile/55.png" alt="" class="attachment-number-icon">
<span class="attachment-number-text">541人已完成</span>
</div>
</div>
<!-- 作业按钮区域 -->
<div class="assignment-buttons">
<!-- 未完成状态显示两个按钮 -->
<template v-if="assignment.status === '未完成' || assignment.status === '待提交'">
<div class="text-wrapper_8 view-button" @click="showUploadModal(assignment)">
<span class="text_36 text-view">上传作业</span>
</div>
<!-- 查看详细 -->
<div class="text-wrapper_8 details-button" @click="viewAssignmentDetail(assignment)">
<span class="text_36">查看详情</span>
</div>
</template>
<!-- 已完成状态只显示一个按钮 -->
<template v-else>
<div class="text-wrapper_8 details-button" @click="viewAssignmentDetail(assignment)">
<span class="text_36">查看详情</span>
</div>
</template>
</div>
</div>
</div>
</div>
<!-- 考试内容 -->
<div v-else-if="isExamTab" class="exam-content">
<!-- 考试筛选标签 -->
<div class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activeExamTab === 'all' }"
@click="handleExamTabChange('all')">全部考试</span>
<span class="text_13" :class="{ active: activeExamTab === 'upcoming' }"
@click="handleExamTabChange('upcoming')">未开始</span>
<span class="text_14" :class="{ active: activeExamTab === 'ongoing' }"
@click="handleExamTabChange('ongoing')">进行中</span>
<span class="text_15" :class="{ active: activeExamTab === 'finished' }"
@click="handleExamTabChange('finished')">已结束</span>
</div>
<!-- 分割线 -->
<div class="course-divider"></div>
<!-- 考试列表 -->
<div class="exam-grid">
<div v-for="exam in filteredExams" :key="exam.id" class="exam-card">
<!-- 考试标题 -->
<div class="exam-title">{{ exam.title }}</div>
<!-- 分数显示 -->
<div class="exam-score-badge" v-if="exam.score !== null">
<span class="score-text">{{ exam.score }}<span></span></span>
</div>
<!-- 考试信息 -->
<div class="exam-details">
<div class="exam-meta-item">
<span class="meta-label">考试日期</span>
<span class="meta-value">{{ exam.examDate }}</span>
</div>
<div class="exam-meta-item">
<span class="meta-label">考试时间</span>
<span class="meta-value">{{ exam.duration }}分钟</span>
</div>
<div class="exam-meta-item">
<span class="meta-label">考试题量</span>
<span class="meta-value">{{ exam.questionCount }}</span>
</div>
</div>
<!-- 考试描述 -->
<div class="exam-description">
{{ exam.description }}
</div>
<!-- 底部操作区域 -->
<div class="exam-footer">
<div class="exam-action-right">
<button v-if="exam.status === 'upcoming'" class="action-btn upcoming-btn" @click="startExam(exam.id)">
未开始
</button>
<button v-else-if="exam.status === 'ongoing'" class="action-btn ongoing-btn"
@click="continueExam(exam.id)">
开始考试
</button>
<button v-else-if="exam.status === 'finished'" class="action-btn finished-btn"
@click="viewExamResult(exam.id)">
查看详情
</button>
</div>
<div class="exam-status-left">
<span class="exam-status-text">{{ getExamStatusText(exam.status) }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 练习内容 -->
<PracticeContent v-else-if="isPracticeTab"></PracticeContent>
<!-- 活动内容 -->
<ActivityContent v-else-if="isActivityTab"></ActivityContent>
<!-- 关注内容 -->
<div v-else-if="isFollowsTab" class="follows-content">
<!-- 关注筛选标签 -->
<div class="text-wrapper_1 flex-row">
<span class="text_12" :class="{ active: activeFollowsTab === 'all' }"
@click="handleFollowsTabChange('all')">全部关注</span>
<span class="text_13" :class="{ active: activeFollowsTab === 'recent' }"
@click="handleFollowsTabChange('recent')">最近关注</span>
<span class="text_14" :class="{ active: activeFollowsTab === 'frequent' }"
@click="handleFollowsTabChange('frequent')">最常访问</span>
</div>
<!-- 关注分割线 -->
<div class="course-divider"></div>
<!-- 关注列表 -->
<div class="follows-list">
<div v-for="follow in filteredFollows" :key="follow.id" class="follow-item">
<!-- 用户头像 -->
<div class="follow-avatar">
<img :src="follow.avatar" :alt="follow.name" class="avatar-image" />
</div>
<!-- 右侧信息区域 -->
<div class="follow-right-content">
<!-- 用户信息 -->
<div class="follow-info">
<div class="follow-name">{{ follow.name }}</div>
<div class="follow-description">{{ follow.description }}</div>
</div>
<!-- 关注操作 -->
<div class="follow-actions">
<button class="follow-btn"
:class="{ 'following': follow.isFollowing, 'not-following': !follow.isFollowing }"
@click="toggleFollow(follow.id)">
{{ follow.isFollowing ? '已关注' : '取消关注' }}
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 消息内容 -->
<div v-else-if="isMessageTab" class="message-content">
<!-- 消息筛选标签 -->
<div class="message-header">
<div class="message-tabs">
<span class="message-tab-item" :class="{ active: activeMessageTab === 'instant' }"
@click="handleMessageTabChange('instant')">
即时消息
<span v-if="instantMessageCount > 0" class="message-count">{{ instantMessageCount }}</span>
</span>
<span class="message-tab-item" :class="{ active: activeMessageTab === 'comment' }"
@click="handleMessageTabChange('comment')">
评论和@
<span v-if="commentMessageCount > 0" class="message-count">{{ commentMessageCount }}</span>
</span>
<span class="message-tab-item" :class="{ active: activeMessageTab === 'like' }"
@click="handleMessageTabChange('like')">
点赞
<span v-if="likeMessageCount > 0" class="message-count">{{ likeMessageCount }}</span>
</span>
<span class="message-tab-item" :class="{ active: activeMessageTab === 'system' }"
@click="handleMessageTabChange('system')">
系统消息
<span v-if="systemMessageCount > 0" class="message-count">{{ systemMessageCount }}</span>
</span>
</div>
<div class="message-actions">
<span class="action-link" :class="{ disabled: markingAllAsRead }" @click="markAllAsRead">
<img src="/images/profile/read.png" alt="全部已读" class="action-icon">
{{ markingAllAsRead ? '标记中...' : '全部已读' }}
</span>
<span class="action-link" :class="{ active: sortByTime !== 'none' }" @click="toggleSortByTime">
<img src="/images/profile/time.png" alt="按时间" class="action-icon">
{{ getSortText() }}
</span>
</div>
</div>
<!-- 消息列表 -->
<div class="message-list">
<!-- 即时消息 -->
<div v-if="activeMessageTab === 'instant'" class="instant-message-container">
<InstantMessage />
</div>
<!-- 评论和@消息 -->
<div v-else-if="activeMessageTab === 'comment'">
<!-- 加载状态 -->
<div v-if="commentMessagesLoading" class="loading-container">
<div class="loading-text">正在加载评论和@消息...</div>
</div>
<!-- 评论和@消息列表 -->
<div v-else-if="filteredMessages.length > 0" class="message-list-container">
<div v-for="msg in filteredMessages" :key="msg.id" class="message-item">
<!-- 未读标识 - 移到右上角 -->
<div v-if="!msg.isRead" class="unread-indicator-top-right"></div>
<!-- 消息内容 -->
<div class="message-main">
<!-- 用户头像和信息 -->
<div class="message-user">
<img :src="msg.senderAvatar" class="image_22" />
<div class="user-info">
<div class="user-content">
<span class="user-name">{{ msg.senderName }}</span>
<span class="action-type">{{ getActionText(msg.type) }}</span>
<span class="message-content">{{ msg.content }}</span>
</div>
<div class="course-info-container">
<span class="course-label">回复了我的课程:</span>
<span class="course-name">《{{ msg.courseName }}》</span>
</div>
</div>
<div class="message-time">{{ msg.date }}</div>
</div>
<!-- 操作按钮 -->
<div class="message-actions-row">
<button class="message-action-btn reply-btn" @click="startReply(msg.id)">
<img src="/images/profile/reply.png" alt="回复" class="action-icon">
回复
</button>
<button class="message-action-btn delete-btn">
<img src="/images/profile/del.png" alt="删除" class="action-icon">
删除
</button>
<button class="message-action-btn report-btn">
<img src="/images/profile/report.png" alt="举报" class="action-icon">
举报
</button>
</div>
<!-- 回复区域 -->
<div v-if="replyingMessageId === msg.id" class="reply-section">
<div class="reply-input-container">
<n-input v-model:value="replyContent" type="textarea" placeholder="回复该评论" :rows="3"
class="reply-input" />
<div class="reply-actions">
<button class="send-btn" @click="sendReply(msg.id)">发送</button>
</div>
</div>
</div>
<!-- 已有回复显示 -->
<div v-if="msg.hasReply && msg.replyContent" class="existing-reply">
<div class="reply-content">{{ msg.replyContent }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-else class="empty-state">
<div class="empty-text">暂无评论和@消息</div>
</div>
</div>
<!-- 点赞消息 -->
<div v-else-if="activeMessageTab === 'like'">
<!-- 加载状态 -->
<div v-if="likeMessagesLoading" class="loading-container">
<div class="loading-text">正在加载点赞消息...</div>
</div>
<!-- 点赞消息列表 -->
<div v-else-if="filteredLikeMessages.length > 0">
<div v-for="like in filteredLikeMessages" :key="'like-' + like.id" class="like-message-item">
<!-- 未读标识 -->
<div v-if="!like.isRead" class="unread-indicator-top-right"></div>
<!-- 点赞消息内容 -->
<div class="like-message-main">
<div class="like-message-user">
<img :src="like.userAvatar" class="image_22" />
<div class="like-info">
<div class="user-content">
<span class="user-name">{{ like.userName }}</span>
<span class="action-type">{{ getLikeActionText(like.type) }}</span>
<span class="message-content" v-if="like.content">{{ like.content }}</span>
</div>
<div class="course-info-container" v-if="like.courseName">
<span class="course-label">课程:</span>
<span class="course-name">《{{ like.courseName }}》</span>
</div>
</div>
<div class="message-time">{{ like.date }}</div>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-else class="empty-state">
<div class="empty-text">暂无点赞消息</div>
</div>
</div>
<!-- 系统消息 -->
<div v-else-if="activeMessageTab === 'system'">
<!-- 加载状态 -->
<div v-if="systemMessagesLoading" class="loading-container">
<div class="loading-text">正在加载系统消息...</div>
</div>
<!-- 系统消息列表 -->
<div v-else-if="filteredSystemMessages.length > 0 && !showMessageDetail">
<div v-for="sysMsg in filteredSystemMessages" :key="'sys-' + sysMsg.id" class="system-message-item">
<!-- 系统消息内容 -->
<div class="system-message-main">
<!-- 系统图标和信息 -->
<div class="system-message-user">
<div class="system-icon"></div>
<div class="system-info">
<div class="system-title">{{ sysMsg.title }}</div>
<div class="system-subtitle-row">
<span class="system-subtitle">{{ sysMsg.subtitle }}</span>
<span class="system-detail-link" @click="goToSystemMessageDetail(sysMsg)">查看详情></span>
</div>
</div>
<div class="system-time">{{ sysMsg.date }}</div>
</div>
</div>
</div>
</div>
<!-- 消息详情 -->
<div v-else-if="showMessageDetail && selectedMessage" class="message-detail-container">
<div class="message-detail-header">
<button class="back-btn" @click="closeMessageDetail">← 返回</button>
<h3 class="detail-title">系统消息详情</h3>
</div>
<div class="message-detail-content">
<div class="message-detail-info">
<div class="message-detail-icon">
<div class="system-icon"></div>
</div>
<div class="message-detail-text">
<h4 class="message-detail-title">{{ selectedMessage.title }}</h4>
<div class="message-detail-time">{{ selectedMessage.date }}</div>
</div>
</div>
<div class="message-detail-body">
<div class="message-detail-subtitle">{{ selectedMessage.subtitle }}</div>
</div>
<div class="message-detail-actions">
<button class="action-btn mark-read-btn" :disabled="selectedMessage.isRead || markingAsRead"
@click="markMessageAsRead(selectedMessage.id)">
{{ selectedMessage.isRead ? '已读' : (markingAsRead ? '标记中...' : '标记为已读') }}
</button>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-else class="empty-state">
<div class="empty-text">暂无系统消息</div>
</div>
</div>
</div>
</div>
<!-- 我的资料页面 -->
<MaterialsContent v-else-if="activeTab === 'materials'"></MaterialsContent>
<!-- 我的下载页面 -->
<div v-else-if="activeTab === 'download'" class="download-content"
:class="{ 'in-subdirectory': isInSubDirectory }">
<!-- 下载页面标签页 -->
<div class="download-header">
<div class="download-tabs">
<span class="download-tab-item" :class="{ active: activeDownloadTab === 'courseware' }"
@click="handleDownloadTabChange('courseware')">
课件
</span>
<span class="download-tab-item" :class="{ active: activeDownloadTab === 'certificate' }"
@click="handleDownloadTabChange('certificate')">
证书
</span>
<span class="download-tab-item" :class="{ active: activeDownloadTab === 'resources' }"
@click="handleDownloadTabChange('resources')">
资源库
</span>
<span class="download-tab-item" :class="{ active: activeDownloadTab === 'homework' }"
@click="handleDownloadTabChange('homework')">
作业
</span>
</div>
</div>
<!-- 面包屑导航或筛选和操作区域 -->
<div v-if="isInSubDirectory" class="breadcrumb-controls">
<div class="breadcrumb-nav">
<span class="breadcrumb-text" @click="goBack">{{ currentPath.join(' > ') }} ></span>
<span class="breadcrumb-current">{{ currentPath[currentPath.length - 1] || '文件夹' }}</span>
</div>
</div>
<!-- 筛选和操作区域 -->
<div v-else-if="activeDownloadTab === 'courseware'" class="download-controls">
<div class="download-filters">
<div class="filter-group">
<label class="filter-label">类型:</label>
<select v-model="downloadFilter.type" class="filter-select">
<option value="all">全部</option>
<option value="folder">文件夹</option>
<option value="file">文件</option>
</select>
</div>
<div class="search-group">
<label class="search-label">搜索:</label>
<div class="search-input-container">
<input v-model="downloadFilter.keyword" type="text" class="search-input" placeholder="请输入文件名称" />
<button class="search-btn">
<img src="/images/profile/search.png" alt="搜索图标" class="search-icon" />
</button>
</div>
</div>
</div>
<div class="download-actions">
<span class="file-count">已全部加载共17个文件</span>
<button class="new-folder-btn" @click="createNewFolder">
+ 新建文件夹
</button>
</div>
</div>
<!-- 文件网格 -->
<div class="files-grid"
:class="{ 'subdirectory-grid': isInSubDirectory, 'certificate-grid': activeDownloadTab === 'certificate', 'homework-grid': activeDownloadTab === 'homework' }">
<div v-for="file in filteredDownloadFiles" :key="file.id" class="file-item"
:class="{ 'subdirectory-item': isInSubDirectory }" @dblclick="handleFolderDoubleClick(file)"
@click="handleFileClick(file)">
<div class="file-menu">
<button class="file-menu-btn" @click.stop="toggleFileMenu(file.id)">
<img src="/images/profile/more.png" alt="更多操作" class="more-icon" />
</button>
<div v-if="activeFileMenu === file.id" class="file-menu-dropdown">
<div class="menu-item" @click.stop="renameFile(file.id)">
<img src="/images/profile/edit.png" alt="编辑图标" class="menu-icon" />
<span>编辑</span>
</div>
<div class="menu-item" @click.stop="deleteFile(file.id)">
<img src="/images/profile/del.png" alt="删除图标" class="menu-icon" />
<span>删除</span>
</div>
</div>
</div>
<div class="file-icon">
<img :src="getFileIcon(file.id)" alt="文件图标" class="folder-icon" />
</div>
<div class="file-name">{{ file.name }}</div>
</div>
</div>
</div>
<!-- 其他内容的占位符 -->
<div v-else class="other-content">
<h2>{{ getTabTitle(activeTab) }}</h2>
<p>{{ activeTab }}功能开发中...</p>
</div>
</div>
</div>
</div>
<!-- 上传作业弹窗 -->
<div v-if="showModal" class="custom-modal-overlay" @click="closeUploadModal">
<div class="custom-modal" @click.stop>
<div v-if="currentAssignment" class="modal-content">
<n-form>
<div class="form-item">
<label class="form-label">标题名称 <span class="required">*</span></label>
<input v-model="uploadForm.title" type="text" class="form-input" placeholder="" />
</div>
<n-form-item label="编辑内容" required>
<QuillEditor v-model="uploadForm.content" placeholder="请输入内容" height="400px" />
</n-form-item>
<!-- <n-form-item label="上传文件" required>
<n-upload multiple directory-dnd :custom-request="customRequest" :default-upload="false">
<n-button>选择文件</n-button>
</n-upload>
<div class="file-list" v-if="uploadForm.files.length > 0">
<div v-for="(file, index) in uploadForm.files" :key="index" class="file-item">
<div class="file-icon">
<img src="/images/auth/file.png" alt="文件图标" />
</div>
<div class="file-info">
<div class="file-name">{{ file.name }}</div>
<div class="file-size">{{ (file.size / 1024).toFixed(2) }}KB</div>
</div>
<button class="file-delete" @click="removeFile(index)">🗑️</button>
</div>
</div>
</n-form-item> -->
</n-form>
<div class="modal-footer">
<button type="primary" @click="submitAssignment" style="margin-left: 12px;">确认</button>
<button @click="closeUploadModal" class="cancel">取消</button>
</div>
</div>
</div>
</div>
<!-- 考试详情页面 -->
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onActivated, reactive } from 'vue'
import { useMessage, NInput, NForm, NFormItem } from 'naive-ui'
// import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores/user'
import SafeAvatar from '@/components/common/SafeAvatar.vue'
import QuillEditor from '@/components/common/QuillEditor.vue'
import InstantMessage from '@/components/InstantMessage.vue'
import { useRouter, useRoute } from 'vue-router'
import { MessageApi, type BackendMessageItem } from '@/api'
import MenuApi from '@/api/modules/menu'
import CourseContent from '@/components/profile/CourseContent.vue'
import PracticeContent from '@/components/profile/PracticeContent.vue'
import ActivityContent from '@/components/profile/ActivityContent.vue'
import MaterialsContent from '@/components/profile/MaterialsContent.vue'
// const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
// 轮播图根据语言动态切换
// const bannerImage = computed(() => {
// return locale.value === 'zh' ? '/banners/banner8.png' : '/banners/banner1-en.png'
// })
// const bannerAlt = computed(() => {
// return t('home.banner.alt')
// })
// 定义课程接口
// interface Course {
// id: number
// title: string
// instructor: string
// description: string
// thumbnail: string
// status: 'learning' | 'completed'
// rating?: number
// chapters?: number
// lessons?: number
// duration?: string
// watchedTime?: string
// progress?: number
// }
// 定义作业接口
interface Assignment {
id: number
teacherName: string
teacherAvatar: string
assignTime: string
status: string
title: string
description: string
attachments: Array<{
icon: string
name: string
}>
mainImage: string
}
// 定义考试接口
interface Exam {
id: number
title: string
examDate: string
duration: number
questionCount: number
description: string
status: 'upcoming' | 'ongoing' | 'finished'
score: number | null
}
// 考试题目选项接口
// interface ExamOption {
// id: string
// text: string
// image?: string
// }
// 考试题目接口
// interface ExamQuestion {
// id: number
// type: 'single' | 'multiple'
// title: string
// content: string
// options: ExamOption[]
// correctAnswers: string[]
// userAnswers: string[]
// isCorrect: boolean
// }
// 考试详情接口
// interface ExamDetail {
// id: number
// title: string
// examDate: string
// duration: number
// totalQuestions: number
// correctCount: number
// wrongCount: number
// score: number
// questions: ExamQuestion[]
// }
// 练习接口
// interface Practice {
// id: number
// title: string
// practiceDate: string
// duration: number
// questionCount: number
// description: string
// status: 'ongoing' | 'finished'
// score: number | null
// correctCount: number
// wrongCount: number
// }
// 练习详情接口(复用考试题目结构)
// interface PracticeDetail {
// id: number
// title: string
// practiceDate: string
// duration: number
// totalQuestions: number
// correctCount: number
// wrongCount: number
// score: number
// questions: ExamQuestion[]
// }
// 活动接口
// interface Activity {
// id: number
// title: string
// subtitle: string
// courseTitle: string
// activityDate: string
// duration: number
// activityType: string
// registeredCount: string
// description: string
// status: 'ongoing' | 'finished'
// score: number | null
// }
// 消息接口
interface Message {
id: string
senderName: string
senderAvatar: string
content: string
courseName: string
date: string
isRead: boolean
hasReply: boolean
replyContent?: string
type: 'comment' | 'mention' | 'reply'
}
// 系统消息接口
interface SystemMessage {
id: string
title: string
subtitle: string
date: string
isRead: boolean
}
// 点赞消息接口
interface LikeMessage {
id: string
userName: string
userAvatar: string
type: 'comment' | 'course'
courseName?: string
content?: string
date: string
isRead: boolean
}
const message = useMessage()
const userStore = useUserStore()
// 定义标签页类型
type TabType = 'courses' | 'homework' | 'exam' | 'practice' | 'activity' | 'follows' | 'message' | 'materials' | 'download'
const activeTab = ref<TabType>('courses')
// const activeCourseTab = ref('all')
// 作业筛选状态
const activeHomeworkTab = ref('all')
// 考试筛选状态
const activeExamTab = ref('all')
// 练习筛选状态
// const activePracticeTab = ref('all')
// 活动筛选状态
// const activeActivityTab = ref('all')
// 关注筛选状态
const activeFollowsTab = ref('all')
// 消息相关状态
const activeMessageTab = ref('instant')
const replyingMessageId = ref<string | null>(null)
const replyContent = ref('')
// 系统消息相关状态
const systemMessages = ref<SystemMessage[]>([])
const systemMessagesLoading = ref(false)
const systemMessagesTotal = ref(0)
const systemMessagesPageSize = ref(10)
const systemMessagesCurrentPage = ref(1)
// 点赞消息相关状态
const likeMessages = ref<LikeMessage[]>([])
const likeMessagesLoading = ref(false)
const likeMessagesTotal = ref(0)
const likeMessagesPageSize = ref(10)
const likeMessagesCurrentPage = ref(1)
// 评论和@消息相关状态
const commentMessages = ref<Message[]>([])
const commentMessagesLoading = ref(false)
const commentMessagesTotal = ref(0)
const commentMessagesPageSize = ref(10)
const commentMessagesCurrentPage = ref(1)
// 消息详情状态
const showMessageDetail = ref(false)
const selectedMessage = ref<SystemMessage | null>(null)
const markingAsRead = ref(false)
const markingAllAsRead = ref(false)
// 排序状态
const sortByTime = ref<'asc' | 'desc' | 'none'>('none') // 'asc': 升序, 'desc': 降序, 'none': 不排序
// 消息数量统计
const instantMessageCount = ref(0)
const commentMessageCount = ref(0)
const likeMessageCount = ref(0)
const systemMessageCount = ref(0)
const messageCountsLoading = ref(false)
// 下载相关状态
const activeDownloadTab = ref('courseware')
const activeFileMenu = ref<number | null>(null)
const currentPath = ref<string[]>([]) // 当前路径,用于面包屑
const isInSubDirectory = ref(false) // 是否在子目录中
const isRefreshing = ref(false) // 是否正在刷新
// 下载筛选条件
const downloadFilter = reactive({
type: 'all',
keyword: ''
})
// 下载文件数据
const downloadFiles = reactive([
{ id: 1, name: '图片', type: 'folder', category: 'courseware' },
{ id: 2, name: '文档', type: 'folder', category: 'courseware' },
{ id: 3, name: '视频', type: 'folder', category: 'courseware' },
{ id: 4, name: '样子', type: 'folder', category: 'courseware' },
{ id: 5, name: '音频', type: 'folder', category: 'courseware' },
{ id: 6, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 7, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 8, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 9, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 10, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 11, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 12, name: '文件名称', type: 'folder', category: 'courseware' },
{ id: 13, name: '证书文件1', type: 'folder', category: 'certificate' },
{ id: 14, name: '证书文件2', type: 'folder', category: 'certificate' },
{ id: 16, name: '证书文件2', type: 'folder', category: 'certificate' },
{ id: 17, name: '证书文件2', type: 'folder', category: 'certificate' },
{ id: 18, name: '证书文件2', type: 'folder', category: 'certificate' },
{ id: 15, name: '资源文件1', type: 'folder', category: 'resources' },
{ id: 19, name: '作业文件1', type: 'folder', category: 'homework' },
{ id: 20, name: '作业文件2', type: 'folder', category: 'homework' },
{ id: 21, name: '作业文件3', type: 'folder', category: 'homework' },
{ id: 22, name: '作业文件4', type: 'folder', category: 'homework' },
{ id: 23, name: '作业文件5', type: 'folder', category: 'homework' },
{ id: 24, name: '作业文件6', type: 'folder', category: 'homework' }
])
// 分页相关状态
// const currentPage = ref(1)
// const pageSize = ref(5) // 每页显示5个课程
// 模拟课程数据基于蓝湖UI设计
// const mockCourses: Course[] = [
// {
// id: 1,
// title: '教育心理学的起源',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPngd0745b9ee85d1fd2251cde7df5f13cdc38cfb57ba7f1ca467638a1196af0a4fc',
// status: 'learning',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '10小时20分钟',
// progress: 75
// },
// {
// id: 2,
// title: '现代教育技术应用',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'completed',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '10小时20分钟',
// progress: 100
// },
// {
// id: 3,
// title: '课程设计与开发',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'completed',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '10小时20分钟',
// progress: 100
// },
// {
// id: 4,
// title: '现代教育技术应用',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'learning',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '8小时15分钟',
// progress: 65
// },
// {
// id: 5,
// title: '数字化教学设计',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'learning',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '5小时30分钟',
// progress: 45
// },
// {
// id: 6,
// title: '在线教育平台运营',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'completed',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '12小时43分钟',
// progress: 100
// },
// {
// id: 7,
// title: '教学评估与反馈',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'learning',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '3小时20分钟',
// progress: 25
// },
// {
// id: 8,
// title: '智能教育工具应用',
// instructor: '代方枚 史雯',
// description: '本课程紧跟风向让每一位教师了解并学习使用DeepSeek结合办公自动化职业岗位标准以实际工作任务为引领强调课程内容的易用性和岗位要求的匹配性课程内容与全国计算机等级考试、"1+X"WPS办公应用职业技能等级证书技能…',
// thumbnail: 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng80fbbc0cab537164b6d85a9a7dcea31848f9becbaceafadb4ea618af4fde9554',
// status: 'completed',
// rating: 541,
// chapters: 9,
// lessons: 54,
// duration: '12小时43分钟',
// watchedTime: '12小时43分钟',
// progress: 100
// }
// ]
// 模拟作业数据基于蓝湖UI设计
const mockAssignments: Assignment[] = [
{
id: 1,
teacherName: '张老师',
teacherAvatar: '/images/traings/traing1.png',
assignTime: '2024-01-15',
status: '待提交',
title: '教育心理学课程设计作业',
description: '请根据所学的教育心理学理论,设计一个完整的课程教学方案,包括教学目标、教学内容、教学方法、教学评价等方面。要求理论联系实际,体现现代教育理念。',
attachments: [
{ icon: '/images/traings/traing1.png', name: '作业要求.pdf' },
{ icon: '/images/traings/traing1.png', name: '参考资料.docx' },
{ icon: '/images/traings/traing1.png', name: '模板文件.pptx' },
{ icon: '/images/traings/traing1.png', name: '评分标准.pdf' },
{ icon: '/images/traings/traing1.png', name: '案例分析.xlsx' }
],
mainImage: '/images/traings/traing1.png'
},
{
id: 2,
teacherName: '李老师',
teacherAvatar: '/images/traings/traing1.png',
assignTime: '2024-01-20',
status: '已完成',
title: '现代教育技术应用实践报告',
description: '结合本学期所学的现代教育技术知识,选择一个具体的教学场景,设计并实施一个融合信息技术的教学活动,撰写实践报告。',
attachments: [
{ icon: '/images/traings/traing1.png', name: '实践指导.pdf' },
{ icon: '/images/traings/traing1.png', name: '报告模板.docx' },
{ icon: '/images/traings/traing1.png', name: '技术工具清单.xlsx' },
{ icon: '/images/traings/traing1.png', name: '评价量表.pdf' },
{ icon: '/images/traings/traing1.png', name: '优秀案例.pptx' }
],
mainImage: '/images/traings/traing1.png'
},
{
id: 3,
teacherName: '王老师',
teacherAvatar: '/images/traings/traing1.png',
assignTime: '2024-01-18',
status: '未完成',
title: '数字化教学资源开发',
description: '开发一套完整的数字化教学资源,包括课件制作、视频录制、在线测试设计等,要求体现现代教育技术的应用特点。',
attachments: [
{ icon: '/images/traings/traing1.png', name: '开发指南.pdf' },
{ icon: '/images/traings/traing1.png', name: '技术要求.docx' },
{ icon: '/images/traings/traing1.png', name: '示例资源.zip' }
],
mainImage: '/images/traings/traing1.png'
},
{
id: 4,
teacherName: '刘老师',
teacherAvatar: '/images/traings/traing1.png',
assignTime: '2024-01-10',
status: '已完成',
title: '在线教育平台使用报告',
description: '体验并分析主流在线教育平台的功能特点,撰写详细的使用报告和改进建议。',
attachments: [
{ icon: '/images/traings/traing1.png', name: '平台列表.pdf' },
{ icon: '/images/traings/traing1.png', name: '评价标准.docx' }
],
mainImage: '/images/traings/traing1.png'
}
]
// 模拟考试数据
const mockExams: Exam[] = [
{
id: 1,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'upcoming',
score: null
},
{
id: 2,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'ongoing',
score: null
},
{
id: 3,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'finished',
score: 90
},
{
id: 4,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'upcoming',
score: null
},
{
id: 5,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'ongoing',
score: null
},
{
id: 6,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'finished',
score: 90
},
{
id: 7,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'upcoming',
score: null
},
{
id: 8,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'finished',
score: 90
},
{
id: 9,
title: 'C++语言程序设计基础考试',
examDate: '2025-07-18 10:00',
duration: 120,
questionCount: 100,
description: '考试涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
status: 'ongoing',
score: null
}
]
// 模拟考试详情数据
// const mockExamDetails: { [key: number]: ExamDetail } = {
// 3: {
// id: 3,
// title: 'C++语言程序设计基础考试',
// examDate: '2025-07-30 12:00',
// duration: 100,
// totalQuestions: 120,
// correctCount: 90,
// wrongCount: 10,
// score: 98,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书' },
// { id: 'B', text: 'B.应急处理措施' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// },
// {
// id: 2,
// type: 'multiple',
// title: '[多选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书', image: '/images/exam/option-a.png' },
// { id: 'B', text: 'B.应急处理措施', image: '/images/exam/option-b.png' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证', image: '/images/exam/option-d.png' },
// { id: 'E', text: 'E.产品标签' }
// ],
// correctAnswers: ['A', 'B'],
// userAnswers: ['A', 'B'],
// isCorrect: true
// }
// ]
// },
// 6: {
// id: 6,
// title: 'C++语言程序设计基础考试',
// examDate: '2025-07-30 12:00',
// duration: 100,
// totalQuestions: 120,
// correctCount: 90,
// wrongCount: 10,
// score: 98,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书' },
// { id: 'B', text: 'B.应急处理措施' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// },
// {
// id: 2,
// type: 'multiple',
// title: '[多选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书', image: '/images/exam/option-a.png' },
// { id: 'B', text: 'B.应急处理措施', image: '/images/exam/option-b.png' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证', image: '/images/exam/option-d.png' },
// { id: 'E', text: 'E.产品标签' }
// ],
// correctAnswers: ['A', 'B'],
// userAnswers: ['A', 'B'],
// isCorrect: true
// }
// ]
// },
// 8: {
// id: 8,
// title: 'C++语言程序设计基础考试',
// examDate: '2025-07-30 12:00',
// duration: 100,
// totalQuestions: 120,
// correctCount: 90,
// wrongCount: 10,
// score: 98,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书' },
// { id: 'B', text: 'B.应急处理措施' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// },
// {
// id: 2,
// type: 'multiple',
// title: '[多选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书', image: '/images/exam/option-a.png' },
// { id: 'B', text: 'B.应急处理措施', image: '/images/exam/option-b.png' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证', image: '/images/exam/option-d.png' },
// { id: 'E', text: 'E.产品标签' }
// ],
// correctAnswers: ['A', 'B'],
// userAnswers: ['A', 'B'],
// isCorrect: true
// }
// ]
// },
// 9: {
// id: 9,
// title: 'C++语言程序设计基础考试',
// examDate: '2025-07-30 12:00',
// duration: 100,
// totalQuestions: 120,
// correctCount: 90,
// wrongCount: 10,
// score: 98,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书' },
// { id: 'B', text: 'B.应急处理措施' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// },
// {
// id: 2,
// type: 'multiple',
// title: '[多选题]',
// content: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装(包括外包装)上标识,或者将技术与包装内危险化学品相符的化学品()和安全技术说明书。',
// options: [
// { id: 'A', text: 'A.操作规范说明书', image: '/images/exam/option-a.png' },
// { id: 'B', text: 'B.应急处理措施', image: '/images/exam/option-b.png' },
// { id: 'C', text: 'C.安全标签' },
// { id: 'D', text: 'D.产品合格证', image: '/images/exam/option-d.png' },
// { id: 'E', text: 'E.产品标签' }
// ],
// correctAnswers: ['A', 'B'],
// userAnswers: ['A', 'B'],
// isCorrect: true
// }
// ]
// }
// }
// 模拟练习数据
// 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: 90,
// correctCount: 18,
// wrongCount: 15
// },
// {
// id: 3,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-18 10:00',
// duration: 88,
// questionCount: 100,
// description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
// status: 'finished',
// score: 90,
// correctCount: 18,
// wrongCount: 15
// },
// {
// id: 4,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-18 10:00',
// duration: 120,
// questionCount: 100,
// description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
// status: 'ongoing',
// score: null,
// correctCount: 18,
// wrongCount: 15
// },
// {
// id: 5,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-18 10:00',
// duration: 88,
// questionCount: 100,
// description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
// status: 'finished',
// score: 90,
// correctCount: 18,
// wrongCount: 15
// },
// {
// id: 6,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-18 10:00',
// duration: 88,
// questionCount: 100,
// description: '练习涵盖C++基础语法、面向对象编程、数据结构等核心内容旨在全面评估学生对C++编程语言的掌握程度和实际应用能力。',
// status: 'finished',
// score: 90,
// correctCount: 18,
// wrongCount: 15
// }
// ]
// 模拟活动数据
// const mockActivities: Activity[] = [
// {
// id: 1,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 120,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'ongoing',
// score: null
// },
// {
// id: 2,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 88,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'finished',
// score: null
// },
// {
// id: 3,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 88,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'finished',
// score: null
// },
// {
// id: 4,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 120,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'ongoing',
// score: null
// },
// {
// id: 5,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 88,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'finished',
// score: null
// },
// {
// id: 6,
// title: '2025计算机二级',
// subtitle: 'C语言讲练综合班',
// courseTitle: '2025计算机二级C语言讲练综合班',
// activityDate: '2025-07-18 10:00',
// duration: 88,
// activityType: '激励类活动',
// registeredCount: '100/200',
// description: 'C++语言程序设计基础练习题涵盖C++基础语法、面向对象编程、数据结构等核心内容。',
// status: 'finished',
// score: null
// }
// ]
// 模拟关注数据
const mockFollows = [
{
id: 1,
name: '张教授',
role: '教育心理学教授',
description: '专注于教育心理学研究,发表多篇学术论文',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'recent',
visitCount: 15,
isFollowing: true
},
{
id: 2,
name: '李老师',
role: '数学教师',
description: '拥有10年教学经验擅长数学教学方法创新',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'recent',
visitCount: 8,
isFollowing: false
},
{
id: 3,
name: '王同学',
role: '学习伙伴',
description: '北京理工大学计算机学院教授计算机计算机计算',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'frequent',
visitCount: 25,
isFollowing: true
},
{
id: 4,
name: '刘同学',
role: '学习伙伴',
description: '学习小组组长,组织能力强',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'frequent',
visitCount: 32,
isFollowing: false
},
{
id: 5,
name: '陈博士',
role: '计算机科学教授',
description: '人工智能与机器学习专家发表论文50余篇',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'recent',
visitCount: 12,
isFollowing: true
},
{
id: 6,
name: '赵同学',
role: '学习伙伴',
description: '编程爱好者,经常分享学习心得',
avatar: '/images/profile/avater.png',
type: 'all',
category: 'frequent',
visitCount: 28,
isFollowing: false
}
]
// 模拟消息数据 - 已移除使用API数据
// 模拟点赞消息数据 - 已移除使用API数据
// 模拟练习详情数据
// const mockPracticeDetails: { [key: number]: PracticeDetail } = {
// 2: {
// id: 2,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-30 14:00',
// duration: 88,
// totalQuestions: 100,
// correctCount: 18,
// wrongCount: 15,
// score: 90,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: 'C++中,下列哪个关键字用于定义类?',
// options: [
// { id: 'A', text: 'A.struct' },
// { id: 'B', text: 'B.class' },
// { id: 'C', text: 'C.union' },
// { id: 'D', text: 'D.enum' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// },
// {
// id: 2,
// type: 'multiple',
// title: '[多选题]',
// content: 'C++中,下列哪些是面向对象编程的特性?',
// options: [
// { id: 'A', text: 'A.封装' },
// { id: 'B', text: 'B.继承' },
// { id: 'C', text: 'C.多态' },
// { id: 'D', text: 'D.递归' },
// { id: 'E', text: 'E.抽象' }
// ],
// correctAnswers: ['A', 'B', 'C', 'E'],
// userAnswers: ['A', 'B', 'C'],
// isCorrect: false
// }
// ]
// },
// 3: {
// id: 3,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-30 14:00',
// duration: 88,
// totalQuestions: 100,
// correctCount: 18,
// wrongCount: 15,
// score: 90,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: 'C++中,下列哪个关键字用于定义类?',
// options: [
// { id: 'A', text: 'A.struct' },
// { id: 'B', text: 'B.class' },
// { id: 'C', text: 'C.union' },
// { id: 'D', text: 'D.enum' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// }
// ]
// },
// 5: {
// id: 5,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-30 14:00',
// duration: 88,
// totalQuestions: 100,
// correctCount: 18,
// wrongCount: 15,
// score: 90,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: 'C++中,下列哪个关键字用于定义类?',
// options: [
// { id: 'A', text: 'A.struct' },
// { id: 'B', text: 'B.class' },
// { id: 'C', text: 'C.union' },
// { id: 'D', text: 'D.enum' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// }
// ]
// },
// 6: {
// id: 6,
// title: 'C++语言程序设计基础练习题',
// practiceDate: '2025-07-30 14:00',
// duration: 88,
// totalQuestions: 100,
// correctCount: 18,
// wrongCount: 15,
// score: 90,
// questions: [
// {
// id: 1,
// type: 'single',
// title: '[单选题]',
// content: 'C++中,下列哪个关键字用于定义类?',
// options: [
// { id: 'A', text: 'A.struct' },
// { id: 'B', text: 'B.class' },
// { id: 'C', text: 'C.union' },
// { id: 'D', text: 'D.enum' }
// ],
// correctAnswers: ['B'],
// userAnswers: ['B'],
// isCorrect: true
// }
// ]
// }
// }
// 获取筛选后的所有课程
// const allFilteredCourses = computed(() => {
// if (activeCourseTab.value === 'learning') {
// return mockCourses.filter(course => course.status === 'learning')
// } else if (activeCourseTab.value === 'completed') {
// return mockCourses.filter(course => course.status === 'completed')
// }
// return mockCourses
// })
// 计算总页数
// const totalPages = computed(() => {
// return Math.ceil(allFilteredCourses.value.length / pageSize.value)
// })
// 当前页显示的课程
// const filteredCourses = computed(() => {
// const start = (currentPage.value - 1) * pageSize.value
// const end = start + pageSize.value
// return allFilteredCourses.value.slice(start, end)
// })
// 获取筛选后的作业
const filteredAssignments = computed(() => {
if (activeHomeworkTab.value === 'pending') {
return mockAssignments.filter(assignment => assignment.status === '待提交' || assignment.status === '未完成')
} else if (activeHomeworkTab.value === 'completed') {
return mockAssignments.filter(assignment => assignment.status === '已完成' || assignment.status === '已完成')
}
return mockAssignments
})
// 获取筛选后的考试
const filteredExams = computed(() => {
if (activeExamTab.value === 'upcoming') {
return mockExams.filter(exam => exam.status === 'upcoming')
} else if (activeExamTab.value === 'ongoing') {
return mockExams.filter(exam => exam.status === 'ongoing')
} else if (activeExamTab.value === 'finished') {
return mockExams.filter(exam => exam.status === 'finished')
}
return mockExams
})
// 获取筛选后的练习
// 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 filteredActivities = computed(() => {
// if (activeActivityTab.value === 'ongoing') {
// return mockActivities.filter(activity => activity.status === 'ongoing')
// } else if (activeActivityTab.value === 'finished') {
// return mockActivities.filter(activity => activity.status === 'finished')
// }
// return mockActivities
// })
// 获取筛选后的关注
const filteredFollows = computed(() => {
if (activeFollowsTab.value === 'recent') {
return mockFollows.filter(follow => follow.category === 'recent')
} else if (activeFollowsTab.value === 'frequent') {
return mockFollows.filter(follow => follow.category === 'frequent')
}
return mockFollows
})
// 计算属性用于模板中的条件判断
const isCoursesTab = computed(() => activeTab.value === 'courses')
const isHomeworkTab = computed(() => activeTab.value === 'homework')
const isExamTab = computed(() => activeTab.value === 'exam')
const isPracticeTab = computed(() => activeTab.value === 'practice')
const isActivityTab = computed(() => activeTab.value === 'activity')
const isFollowsTab = computed(() => activeTab.value === 'follows')
const isMessageTab = computed(() => activeTab.value === 'message')
// 处理菜单选择
const handleMenuSelect = (key: TabType) => {
activeTab.value = key
router.push(`/profile/${key}`)
// message.info(`切换到${getTabTitle(key)}`)
}
// 获取状态文本
// const getStatusText = (status: string) => {
// switch (status) {
// case 'learning':
// return '学习中'
// case 'completed':
// return '已完结'
// default:
// return '未开始'
// }
// }
// 跳转到课程
// const goToCourse = (courseId: number) => {
// message.info(`跳转到课程 ${courseId}`)
// }
// 分页器方法
// const goToPage = (page: number | string) => {
// if (typeof page === 'number') {
// if (page >= 1 && page <= totalPages.value) {
// currentPage.value = page
// }
// } else {
// switch (page) {
// case 'first':
// if (currentPage.value > 1) {
// currentPage.value = 1
// }
// break
// case 'prev':
// if (currentPage.value > 1) {
// currentPage.value--
// }
// break
// case 'next':
// if (currentPage.value < totalPages.value) {
// currentPage.value++
// }
// break
// case 'last':
// if (currentPage.value < totalPages.value) {
// currentPage.value = totalPages.value
// }
// break
// }
// }
// }
// 监听筛选变化,重置到第一页
// const handleCourseTabChange = (tab: string) => {
// activeCourseTab.value = tab
// currentPage.value = 1
// }
// 处理作业筛选变化
const handleHomeworkTabChange = (tab: string) => {
activeHomeworkTab.value = tab
}
// 处理考试筛选变化
const handleExamTabChange = (tab: string) => {
activeExamTab.value = tab
}
// 处理练习筛选变化
// const handlePracticeTabChange = (tab: string) => {
// activePracticeTab.value = tab
// }
// 处理活动筛选变化
// const handleActivityTabChange = (tab: string) => {
// activeActivityTab.value = tab
// }
// 处理关注筛选变化
const handleFollowsTabChange = (tab: string) => {
activeFollowsTab.value = tab
}
// 获取考试状态文本
const getExamStatusText = (status: string) => {
switch (status) {
case 'upcoming':
return '未开始'
case 'ongoing':
return '进行中'
case 'finished':
return '已结束'
default:
return '未知状态'
}
}
// 开始考试
const startExam = (examId: number) => {
message.info(`开始考试 ${examId}`)
}
// 继续考试
const continueExam = (examId: number) => {
// message.info(`继续考试 ${examId}`)
router.push(`/exam/notice/${examId}`)
}
// 查看考试结果
const viewExamResult = (examId: number) => {
router.push(`/exam-detail/${examId}?source=exam`)
}
// 继续练习
// const continuePractice = (practiceId: number) => {
// message.info(`继续练习 ${practiceId}`)
// }
// 查看练习详情
// const viewPracticeDetail = (practiceId: number) => {
// router.push(`/exam-detail/${practiceId}?source=practice`)
// }
// 继续活动
// const continueActivity = (id: number) => {
// console.log('继续活动:', id)
// message.info(`继续活动 ${id}`)
// }
// 查看活动详情
// const viewActivityDetail = (id: number) => {
// router.push(`/activity/${id}`)
// }
// 切换关注状态
const toggleFollow = (id: number) => {
const follow = mockFollows.find(f => f.id === id)
if (follow) {
follow.isFollowing = !follow.isFollowing
const action = follow.isFollowing ? '关注' : '取消关注'
message.info(`已${action}用户 ${follow.name}`)
}
}
// 加载消息数量统计
const loadMessageCounts = async () => {
try {
messageCountsLoading.value = true
console.log('🔄 开始加载消息数量统计...')
console.log('📊 当前消息数量状态:', {
instantMessageCount: instantMessageCount.value,
commentMessageCount: commentMessageCount.value,
likeMessageCount: likeMessageCount.value,
systemMessageCount: systemMessageCount.value
})
// 并行加载各类消息数量
const results = await Promise.allSettled([
loadInstantMessageCount(),
loadCommentMessageCount(),
loadLikeMessageCount(),
loadSystemMessageCount()
])
console.log('📊 消息数量加载结果:', results)
console.log('📊 加载后的消息数量状态:', {
instantMessageCount: instantMessageCount.value,
commentMessageCount: commentMessageCount.value,
likeMessageCount: likeMessageCount.value,
systemMessageCount: systemMessageCount.value
})
// 添加调试信息:检查界面显示的数量
console.log('🔍 界面显示检查:')
console.log(' - 即时消息显示:', instantMessageCount.value > 0 ? instantMessageCount.value : '不显示')
console.log(' - 评论和@显示:', commentMessageCount.value > 0 ? commentMessageCount.value : '不显示')
console.log(' - 点赞显示:', likeMessageCount.value > 0 ? likeMessageCount.value : '不显示')
console.log(' - 系统消息显示:', systemMessageCount.value > 0 ? systemMessageCount.value : '不显示')
} catch (error) {
console.error('❌ 加载消息数量失败:', error)
} finally {
messageCountsLoading.value = false
}
}
// 加载即时消息数量
const loadInstantMessageCount = async () => {
try {
console.log('🔍 开始加载即时消息数量...')
// 由于即时消息接口可能不存在,使用备用方案
instantMessageCount.value = 0
console.log('✅ 即时消息数量设置为:', instantMessageCount.value)
} catch (error) {
console.error('❌ 获取即时消息数量失败:', error)
instantMessageCount.value = 0
}
}
// 加载评论和@消息数量
const loadCommentMessageCount = async () => {
try {
console.log('🔍 开始加载评论和@消息数量...')
const response = await MessageApi.getCommentsAtMessages({ current: 1, size: 100 })
console.log('🔍 评论和@消息数量API响应:', response)
if (response.data && response.data.result && response.data.result.records) {
// 只统计未读消息数量
const unreadCount = response.data.result.records.filter((item: any) => item.readFlag === 0).length
commentMessageCount.value = unreadCount
console.log('✅ 评论和@消息未读数量:', commentMessageCount.value, '(总数:', response.data.result.total, ')')
} else {
commentMessageCount.value = 0
console.log('⚠️ 评论和@消息数量API返回数据格式不正确设置为0')
}
} catch (error) {
console.error('❌ 获取评论消息数量失败:', error)
commentMessageCount.value = 0
}
}
// 加载点赞消息数量
const loadLikeMessageCount = async () => {
try {
console.log('🔍 开始加载点赞消息数量...')
const response = await MessageApi.getLikesMessages({ current: 1, size: 100 })
console.log('🔍 点赞消息数量API响应:', response)
if (response.data && response.data.result && response.data.result.records) {
// 只统计未读消息数量
const unreadCount = response.data.result.records.filter((item: any) => item.readFlag === 0).length
likeMessageCount.value = unreadCount
console.log('✅ 点赞消息未读数量:', likeMessageCount.value, '(总数:', response.data.result.total, ')')
} else {
likeMessageCount.value = 0
console.log('⚠️ 点赞消息数量API返回数据格式不正确设置为0')
}
} catch (error) {
console.error('❌ 获取点赞消息数量失败:', error)
likeMessageCount.value = 0
}
}
// 加载系统消息数量
const loadSystemMessageCount = async () => {
try {
console.log('🔍 开始加载系统消息数量...')
const response = await MessageApi.getSystemMessages({ pageNo: 1, pageSize: 100 })
console.log('🔍 系统消息数量API响应:', response)
if (response.data && response.data.records) {
// 只统计未读消息数量
const unreadCount = response.data.records.filter((item: any) => item.readFlag === 0).length
systemMessageCount.value = unreadCount
console.log('✅ 系统消息未读数量:', systemMessageCount.value, '(总数:', response.data.total, ')')
} else {
systemMessageCount.value = 0
console.log('⚠️ 系统消息数量API返回数据格式不正确设置为0')
}
} catch (error) {
console.error('❌ 获取系统消息数量失败:', error)
systemMessageCount.value = 0
}
}
// 消息相关方法
const handleMessageTabChange = (tab: string) => {
activeMessageTab.value = tab
// 每次切换消息标签时,都加载消息数量统计
loadMessageCounts()
// 当切换到评论和@消息标签时,加载评论和@消息数据
if (tab === 'comment') {
loadCommentMessages()
}
// 当切换到点赞消息标签时,加载点赞消息数据
if (tab === 'like') {
loadLikeMessages()
}
// 当切换到系统消息标签时,加载系统消息数据
if (tab === 'system') {
loadSystemMessages()
}
}
// 加载评论和@消息数据
const loadCommentMessages = async () => {
try {
commentMessagesLoading.value = true
console.log('🔍 开始加载评论和@消息,页码:', commentMessagesCurrentPage.value)
const response = await MessageApi.getCommentsAtMessages({
current: commentMessagesCurrentPage.value,
size: commentMessagesPageSize.value
})
console.log('🔍 评论和@消息API响应:', response)
if (response.data) {
const result = response.data
console.log('✅ 评论和@消息数据:', result)
if (result.success && result.result) {
commentMessages.value = result.result.records.map(transformCommentMessageData)
commentMessagesTotal.value = result.result.total || result.result.records.length
} else {
console.warn('⚠️ 评论和@消息API返回数据格式不正确:', result)
commentMessages.value = []
commentMessagesTotal.value = 0
}
console.log('✅ 转换后的评论和@消息:', commentMessages.value)
} else {
console.warn('⚠️ 评论和@消息API返回错误:', response)
commentMessages.value = []
commentMessagesTotal.value = 0
}
} catch (error) {
console.error('❌ 加载评论和@消息失败:', error)
commentMessages.value = []
commentMessagesTotal.value = 0
} finally {
commentMessagesLoading.value = false
}
}
// 加载点赞消息数据
const loadLikeMessages = async () => {
try {
likeMessagesLoading.value = true
console.log('🔍 开始加载点赞消息,页码:', likeMessagesCurrentPage.value)
const response = await MessageApi.getLikesMessages({
current: likeMessagesCurrentPage.value,
size: likeMessagesPageSize.value
})
console.log('🔍 点赞消息API响应:', response)
if (response.data) {
const result = response.data
console.log('✅ 点赞消息数据:', result)
if (result.success && result.result) {
likeMessages.value = result.result.records.map(transformLikeMessageData)
likeMessagesTotal.value = result.result.total || result.result.records.length
} else {
console.warn('⚠️ 点赞消息API返回数据格式不正确:', result)
likeMessages.value = []
likeMessagesTotal.value = 0
}
console.log('✅ 转换后的点赞消息:', likeMessages.value)
} else {
console.warn('⚠️ 点赞消息API返回错误:', response)
likeMessages.value = []
likeMessagesTotal.value = 0
}
} catch (error) {
console.error('❌ 加载点赞消息失败:', error)
likeMessages.value = []
likeMessagesTotal.value = 0
} finally {
likeMessagesLoading.value = false
}
}
// 加载系统消息数据
const loadSystemMessages = async () => {
try {
systemMessagesLoading.value = true
console.log('🔍 开始加载系统消息,页码:', systemMessagesCurrentPage.value)
const response = await MessageApi.getSystemMessages({
pageNo: systemMessagesCurrentPage.value,
pageSize: systemMessagesPageSize.value
})
console.log('🔍 系统消息API响应:', response)
if (response.data) {
const result = response.data
console.log('✅ 系统消息数据:', result)
// 检查数据结构可能是直接的records数组或者包含records的对象
if (Array.isArray(result)) {
// 如果直接返回数组
systemMessages.value = result.map(transformSystemMessageData)
systemMessagesTotal.value = result.length
} else if (result.records && Array.isArray(result.records)) {
// 如果返回包含records的对象
systemMessages.value = result.records.map(transformSystemMessageData)
systemMessagesTotal.value = result.total || result.records.length
} else if ((result as any).result && (result as any).result.records && Array.isArray((result as any).result.records)) {
// 如果返回嵌套的result结构
systemMessages.value = (result as any).result.records.map(transformSystemMessageData)
systemMessagesTotal.value = (result as any).result.total || (result as any).result.records.length
} else {
// 其他情况,显示空状态
console.warn('⚠️ 系统消息API返回数据格式不正确:', result)
systemMessages.value = []
systemMessagesTotal.value = 0
}
console.log('✅ 转换后的系统消息:', systemMessages.value)
} else {
console.warn('⚠️ 系统消息API返回错误:', response)
// 显示空状态
systemMessages.value = []
systemMessagesTotal.value = 0
}
} catch (error) {
console.error('❌ 加载系统消息失败:', error)
// 显示空状态
systemMessages.value = []
systemMessagesTotal.value = 0
} finally {
systemMessagesLoading.value = false
}
}
// 查看系统消息详情
const goToSystemMessageDetail = async (msg: SystemMessage) => {
console.log('🔍 查看系统消息详情:', msg)
// 如果点击的是当前选中的消息,则关闭详情
if (selectedMessage.value && selectedMessage.value.id === msg.id) {
showMessageDetail.value = false
selectedMessage.value = null
return
}
selectedMessage.value = msg
showMessageDetail.value = true
// 如果消息未读,自动标记为已读
if (!msg.isRead) {
await markMessageAsRead(msg.id)
}
}
// 标记消息为已读
const markMessageAsRead = async (messageId: string) => {
if (markingAsRead.value) return
markingAsRead.value = true
try {
console.log('📝 标记消息为已读messageId:', messageId)
const response = await MessageApi.markSystemMessageAsRead(messageId)
console.log('📝 标记已读API响应:', response)
// 检查API返回的成功状态
if (response.data && (response.data.success === true || response.data.code === 0)) {
// 更新本地状态
const messageIndex = systemMessages.value.findIndex(msg => msg.id === messageId)
if (messageIndex !== -1) {
systemMessages.value[messageIndex].isRead = true
}
if (selectedMessage.value && selectedMessage.value.id === messageId) {
selectedMessage.value.isRead = true
}
message.success(response.data.message || '已标记为已读')
console.log('✅ 标记已读成功')
} else {
console.log('❌ API返回错误:', response.data)
message.error(response.data?.message || '标记已读失败')
}
} catch (err: any) {
console.error('❌ 标记已读失败:', err)
// 检查是否是网络错误或其他具体错误
if (err.response && err.response.status) {
message.error(`标记已读失败: ${err.response.status} - ${err.response.statusText}`)
} else if (err.message) {
message.error(`标记已读失败: ${err.message}`)
} else {
message.error('标记已读失败,请稍后重试')
}
} finally {
markingAsRead.value = false
}
}
// 关闭消息详情
const closeMessageDetail = () => {
showMessageDetail.value = false
selectedMessage.value = null
}
// 排序后的系统消息
const sortedSystemMessages = computed(() => {
if (sortByTime.value === 'none') {
return systemMessages.value
}
return [...systemMessages.value].sort((a, b) => {
const dateA = new Date(a.date).getTime()
const dateB = new Date(b.date).getTime()
if (sortByTime.value === 'desc') {
return dateB - dateA // 最新在前
} else {
return dateA - dateB // 最旧在前
}
})
})
// 排序后的点赞消息
const sortedLikeMessages = computed(() => {
if (sortByTime.value === 'none') {
return likeMessages.value
}
return [...likeMessages.value].sort((a, b) => {
const dateA = new Date(a.date).getTime()
const dateB = new Date(b.date).getTime()
if (sortByTime.value === 'desc') {
return dateB - dateA // 最新在前
} else {
return dateA - dateB // 最旧在前
}
})
})
// 排序后的评论消息
const sortedCommentMessages = computed(() => {
if (sortByTime.value === 'none') {
return commentMessages.value
}
return [...commentMessages.value].sort((a, b) => {
const dateA = new Date(a.date).getTime()
const dateB = new Date(b.date).getTime()
if (sortByTime.value === 'desc') {
return dateB - dateA // 最新在前
} else {
return dateA - dateB // 最旧在前
}
})
})
// 获取排序状态显示文本
const getSortText = () => {
switch (sortByTime.value) {
case 'desc':
return '按时间 ↓'
case 'asc':
return '按时间 ↑'
default:
return '按时间'
}
}
// 按时间排序
const toggleSortByTime = () => {
// 循环切换排序状态: none -> desc -> asc -> none
if (sortByTime.value === 'none') {
sortByTime.value = 'desc' // 最新在前
} else if (sortByTime.value === 'desc') {
sortByTime.value = 'asc' // 最旧在前
} else {
sortByTime.value = 'none' // 不排序
}
console.log('🔄 切换排序状态:', sortByTime.value)
}
// 全部已读
const markAllAsRead = async () => {
if (markingAllAsRead.value) return
markingAllAsRead.value = true
try {
console.log('📝 开始标记全部消息为已读...')
// 收集所有未读消息的ID
const unreadSystemMessages = systemMessages.value.filter((msg: SystemMessage) => !msg.isRead)
const unreadLikeMessages = likeMessages.value.filter((msg: LikeMessage) => !msg.isRead)
const unreadCommentMessages = commentMessages.value.filter((msg: Message) => !msg.isRead)
console.log('📝 未读消息统计:', {
system: unreadSystemMessages.length,
like: unreadLikeMessages.length,
comment: unreadCommentMessages.length
})
// 批量调用单条标记已读接口
const promises: Promise<any>[] = []
// 标记系统消息为已读
unreadSystemMessages.forEach((msg: SystemMessage) => {
promises.push(MessageApi.markSystemMessageAsRead(msg.id))
})
// 标记点赞消息为已读(使用系统消息接口,因为都是同一个后端接口)
unreadLikeMessages.forEach((msg: LikeMessage) => {
promises.push(MessageApi.markSystemMessageAsRead(msg.id))
})
// 标记评论消息为已读
unreadCommentMessages.forEach((msg: Message) => {
promises.push(MessageApi.markSystemMessageAsRead(msg.id))
})
// 等待所有请求完成
const results = await Promise.allSettled(promises)
console.log('📝 批量标记已读结果:', results)
// 统计成功和失败的数量
const successCount = results.filter(result => result.status === 'fulfilled').length
const failCount = results.filter(result => result.status === 'rejected').length
// 如果没有未读消息,直接返回
if (unreadSystemMessages.length === 0 && unreadLikeMessages.length === 0 && unreadCommentMessages.length === 0) {
message.info('所有消息都已经是已读状态')
console.log('✅ 没有未读消息需要标记')
return
}
if (successCount > 0) {
// 更新所有消息的已读状态
systemMessages.value.forEach((msg: SystemMessage) => {
msg.isRead = true
})
likeMessages.value.forEach((msg: LikeMessage) => {
msg.isRead = true
})
commentMessages.value.forEach((msg: Message) => {
msg.isRead = true
})
// 更新消息数量统计
instantMessageCount.value = 0
commentMessageCount.value = 0
likeMessageCount.value = 0
systemMessageCount.value = 0
if (failCount > 0) {
message.warning(`已标记 ${successCount} 条消息为已读,${failCount} 条失败`)
} else {
message.success(`成功标记 ${successCount} 条消息为已读`)
}
console.log('✅ 全部已读完成,成功:', successCount, '失败:', failCount)
} else {
message.error('标记全部已读失败,请稍后重试')
}
} catch (err: any) {
console.error('❌ 标记全部已读失败:', err)
if (err.response && err.response.status) {
message.error(`标记全部已读失败: ${err.response.status} - ${err.response.statusText}`)
} else if (err.message) {
message.error(`标记全部已读失败: ${err.message}`)
} else {
message.error('标记全部已读失败,请稍后重试')
}
} finally {
markingAllAsRead.value = false
}
}
// 获取操作文本
const getActionText = (type: string) => {
switch (type) {
case 'comment':
return '评论了我'
case 'mention':
return '@了我'
case 'reply':
return '回复了我'
default:
return '评论了我'
}
}
// 获取点赞操作文本
const getLikeActionText = (type: string) => {
switch (type) {
case 'comment':
return '点赞了我的评论'
case 'course':
return '点赞了我'
default:
return '点赞了我'
}
}
// 下载相关方法
const handleDownloadTabChange = (tab: string) => {
activeDownloadTab.value = tab
}
// 筛选后的下载文件
const filteredDownloadFiles = computed(() => {
// 如果在子目录中,返回空数组显示空页面
if (isInSubDirectory.value) {
return []
}
let files = downloadFiles.filter(file => file.category === activeDownloadTab.value)
if (downloadFilter.type !== 'all') {
files = files.filter(file => file.type === downloadFilter.type)
}
if (downloadFilter.keyword) {
files = files.filter(file =>
file.name.toLowerCase().includes(downloadFilter.keyword.toLowerCase())
)
}
return files
})
// 文件菜单操作
const toggleFileMenu = (fileId: number) => {
activeFileMenu.value = activeFileMenu.value === fileId ? null : fileId
}
const handleFileClick = (file: any) => {
// 单击文件时的处理逻辑(如选中文件等)
console.log('单击文件:', file.name)
}
const handleFolderDoubleClick = (file: any) => {
if (file.type === 'folder') {
// 双击文件夹,进入子目录(显示空页面)
isInSubDirectory.value = true
currentPath.value = ['课件', file.name]
console.log('进入文件夹:', file.name)
}
}
const goBack = () => {
// 返回上级目录
isInSubDirectory.value = false
currentPath.value = []
}
const getFileIcon = (fileId?: number) => {
if (activeDownloadTab.value === 'certificate') {
// 证书标签页使用证书图标
return '/images/profile/certificate.jpg'
} else if (activeDownloadTab.value === 'homework') {
// 作业标签页使用不同的作业相关图标
const homeworkImages = [
'/images/profile/word.png',
'/images/profile/pdf.png',
'/images/profile/ppt.png',
'/images/profile/xls.png',
'/images/profile/zip.png',
'/images/profile/word.png'
]
const index = (fileId || 0) % homeworkImages.length
return homeworkImages[index]
} else if (isInSubDirectory.value) {
// 子目录使用子目录图标
return 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPngf45333052202c303acc2c06223c26b820d330459ce2d452a21a3132fbbeab442'
} else {
// 默认文件夹图标
return '/images/profile/folder.png'
}
}
const createNewFolder = () => {
message.info('新建文件夹功能开发中...')
}
const renameFile = (fileId: number) => {
message.info(`重命名文件 ${fileId}`)
activeFileMenu.value = null
}
const deleteFile = (fileId: number) => {
message.info(`删除文件 ${fileId}`)
activeFileMenu.value = null
}
// 开始回复消息
const startReply = (messageId: string) => {
replyingMessageId.value = messageId
replyContent.value = ''
}
// 取消回复
const cancelReply = () => {
replyingMessageId.value = null
replyContent.value = ''
}
// 发送回复
const sendReply = (messageId: string) => {
if (!replyContent.value.trim()) {
message.error('请输入回复内容')
return
}
// 这里可以添加实际的发送逻辑
message.success('回复发送成功')
// 更新消息数据中的回复内容
const messageIndex = commentMessages.value.findIndex(msg => msg.id === messageId)
if (messageIndex !== -1) {
commentMessages.value[messageIndex].replyContent = replyContent.value
commentMessages.value[messageIndex].hasReply = true
}
cancelReply()
}
// 获取筛选后的消息
const filteredMessages = computed(() => {
if (activeMessageTab.value === 'comment') {
return sortedCommentMessages.value
}
return []
})
// 数据转换函数 - 将后端数据转换为前端格式
const transformCommentMessageData = (backendItem: BackendMessageItem): Message => {
let senderName = '未知用户'
let senderAvatar = '/images/profile/default-avatar.png'
let content = ''
let courseName = ''
let type: 'comment' | 'mention' | 'reply' = 'comment'
try {
if (backendItem.msgContent) {
const parsedContent = JSON.parse(backendItem.msgContent)
console.log('🔍 解析评论消息内容:', parsedContent)
if (parsedContent.sender) {
senderName = parsedContent.sender.username || '未知用户'
senderAvatar = parsedContent.sender.avatar || '/images/profile/default-avatar.png'
}
if (parsedContent.entity) {
courseName = parsedContent.entity.title || ''
content = parsedContent.entity.content || ''
// 根据消息类型确定type
if (parsedContent.entity.type === 'comment') {
type = 'comment'
} else if (parsedContent.entity.type === 'mention') {
type = 'mention'
} else if (parsedContent.entity.type === 'reply') {
type = 'reply'
}
}
}
} catch (error) {
console.warn('⚠️ 解析评论消息内容失败:', error)
content = backendItem.msgContent || ''
}
return {
id: String(backendItem.id),
senderName,
senderAvatar,
content,
courseName,
date: backendItem.sendTime || new Date().toLocaleString('zh-CN'),
isRead: backendItem.readFlag === 1,
hasReply: false,
replyContent: '',
type
}
}
const transformLikeMessageData = (backendItem: BackendMessageItem): LikeMessage => {
let userName = '未知用户'
let userAvatar = '/images/profile/default-avatar.png'
let type: 'comment' | 'course' = 'course'
let courseName = ''
let content = ''
try {
if (backendItem.msgContent) {
const parsedContent = JSON.parse(backendItem.msgContent)
console.log('🔍 解析点赞消息内容:', parsedContent)
if (parsedContent.sender) {
userName = parsedContent.sender.username || '未知用户'
userAvatar = parsedContent.sender.avatar || '/images/profile/default-avatar.png'
}
if (parsedContent.entity) {
courseName = parsedContent.entity.title || ''
if (parsedContent.entity.type === 'comment') {
type = 'comment'
content = parsedContent.entity.content || ''
} else {
type = 'course'
}
}
}
} catch (error) {
console.warn('⚠️ 解析点赞消息内容失败:', error)
}
return {
id: String(backendItem.id),
userName,
userAvatar,
type,
courseName,
content,
date: backendItem.sendTime || new Date().toLocaleString('zh-CN'),
isRead: backendItem.readFlag === 1
}
}
const transformSystemMessageData = (backendItem: BackendMessageItem): SystemMessage => {
let title = backendItem.titile || '系统消息'
let subtitle = '暂无内容'
// 解析 msgContent 中的JSON数据
try {
if (backendItem.msgContent) {
const parsedContent = JSON.parse(backendItem.msgContent)
console.log('🔍 解析系统消息内容:', parsedContent)
// 根据消息类型生成标题和内容
if (parsedContent.sender && parsedContent.comment) {
// 评论消息
title = `${parsedContent.sender.username} 评论了你的课程`
subtitle = parsedContent.comment.content || '暂无评论内容'
// 处理 @ 提及
subtitle = processAtMentions(subtitle)
} else if (parsedContent.sender && parsedContent.entity) {
// 其他类型的消息
title = `${parsedContent.sender.username} 的${parsedContent.entity.type}消息`
subtitle = parsedContent.entity.title || '暂无内容'
} else {
// 直接使用原始内容
subtitle = backendItem.msgContent
}
}
} catch (error) {
console.warn('⚠️ 解析系统消息内容失败:', error)
// 如果解析失败,直接使用原始内容
subtitle = backendItem.msgContent || '暂无内容'
}
return {
id: String(backendItem.id),
title,
subtitle,
date: backendItem.sendTime || new Date().toLocaleString('zh-CN'),
isRead: backendItem.readFlag === 1
}
}
// 处理 @ 提及的函数
const processAtMentions = (content: string): string => {
if (!content) return content
// 将 [user:id:username] 格式转换为 @username
return content.replace(/\[user:(\d+):([^\]]+)\]/g, '@$2')
}
// 获取筛选后的系统消息
const filteredSystemMessages = computed(() => {
if (activeMessageTab.value === 'system') {
return sortedSystemMessages.value
}
return []
})
// 获取筛选后的点赞消息
const filteredLikeMessages = computed(() => {
if (activeMessageTab.value === 'like') {
return sortedLikeMessages.value
}
return []
})
// 获取标签标题
const getTabTitle = (tab: TabType) => {
const titles: Record<TabType, string> = {
courses: '我的课程',
homework: '我的作业',
exam: '我的考试',
practice: '我的练习',
activity: '我的活动',
follows: '我的关注',
message: '我的消息',
materials: '我的资料',
download: '我的下载'
}
return titles[tab] || '未知页面'
}
// 上传作业弹窗状态
const showModal = ref(false)
const currentAssignment = ref<Assignment | null>(null)
const uploadForm = reactive({
title: '',
content: '',
files: [] as File[]
})
// 作业详情视图状态
const showDetailView = ref(false)
const detailAssignment = ref<Assignment | null>(null)
// 草稿箱视图状态
const showDraftBoxView = ref(false)
const draftAssignment = ref<Assignment | null>(null)
// 考试详情视图状态
// // 获取答题卡项目的样式类
// const getAnswerItemClass = (index: number) => {
// // 模拟答题状态前10题答对第11-15题答错其余未答
// if (index <= 10) {
// return 'correct'
// } else if (index <= 15) {
// return 'wrong'
// } else {
// return 'unanswered'
// }
// }
// 查看作业详情
const viewAssignmentDetail = (assignment: Assignment) => {
detailAssignment.value = assignment
showDetailView.value = true
showDraftBoxView.value = false
}
// 返回作业列表
// const backToAssignmentList = () => {
// showDetailView.value = false
// detailAssignment.value = null
// }
// 从详情页面跳转到上传作业
const showUploadFromDetail = () => {
showUploadModal(detailAssignment.value!)
}
// 显示草稿箱
const showDraftBox = () => {
// 模拟草稿箱数据,实际应该从后端获取
draftAssignment.value = {
id: 1,
title: '教育心理学课程设计作业',
description: '请根据所学的教育心理学理论,设计一个完整的课程教学方案,包括教学目标、教学内容、教学方法、教学评价等方面。要求理论联系实际,体现现代教育理念。',
teacherName: '张老师',
teacherAvatar: '/images/traings/traing1.png',
assignTime: '2024-01-15',
status: '草稿',
attachments: [
{ icon: '/images/auth/file.png', name: 'file1.pdf' },
{ icon: '/images/auth/file.png', name: 'file2.pdf' }
],
mainImage: '/images/traings/traing1.png'
}
showDraftBoxView.value = true
showDetailView.value = false
}
// 返回作业列表(从草稿箱)
const backFromDraftBox = () => {
showDraftBoxView.value = false
draftAssignment.value = null
}
// 重新编辑草稿
const reEditDraft = () => {
showUploadModal(draftAssignment.value!)
}
// 显示上传作业弹窗
const showUploadModal = (assignment: Assignment) => {
currentAssignment.value = assignment
uploadForm.title = ''
uploadForm.content = ''
uploadForm.files = []
showModal.value = true
}
// 关闭上传作业弹窗
const closeUploadModal = () => {
showModal.value = false
currentAssignment.value = null
}
// 提交作业
const submitAssignment = () => {
if (!uploadForm.title.trim()) {
message.error('请输入标题')
return
}
if (!uploadForm.content.trim()) {
message.error('请输入内容')
return
}
if (uploadForm.files.length === 0) {
message.error('请上传至少一个文件')
return
}
// 这里可以添加实际的提交逻辑
message.success('作业提交成功')
closeUploadModal()
}
const menuItems = ref<any[]>([])
const getMenu = async () => {
try {
const response = await MenuApi.getStudentMenus()
if (response.data && response.data.code === 200) {
menuItems.value = response.data.result
console.log('✅ 获取菜单成功:', menuItems.value)
} else {
menuItems.value = []
}
} catch (error) {
console.error('❌ 获取菜单失败:', error)
menuItems.value = []
}
}
// 计算可见的菜单项
const visibleMenuItems = computed(() => {
return menuItems.value
.filter(menu => menu.izVisible === 1)
})
// 从路径获取菜单tab key
const getMenuTabKey = (path: string): TabType => {
const pathMatch = path.match(/\/profile\/(.+)/)
return pathMatch ? pathMatch[1] as TabType : 'courses'
}
// 获取激活状态的图标
const getActiveIcon = (iconPath: string) => {
if (!iconPath) return iconPath
const lastDotIndex = iconPath.lastIndexOf('.')
if (lastDotIndex === -1) return iconPath + '-active'
const fileName = iconPath.substring(0, lastDotIndex)
const extension = iconPath.substring(lastDotIndex)
return fileName + '-active' + extension
}
onMounted(async () => {
// 初始化
// 检查是否需要刷新
const shouldRefresh = sessionStorage.getItem('refreshProfile')
if (shouldRefresh === 'true') {
isRefreshing.value = true
sessionStorage.removeItem('refreshProfile')
// 延迟刷新,给用户一个视觉反馈
setTimeout(() => {
window.location.reload()
}, 100)
}
// 获取菜单
await getMenu()
const tabKey = <TabType>route.params.tabKey || 'courses'
handleMenuSelect(tabKey)
// 如果默认进入消息标签页,自动加载消息数量统计
if (tabKey === 'message') {
loadMessageCounts()
}
})
onActivated(() => {
// 检查是否需要刷新
const shouldRefresh = sessionStorage.getItem('refreshProfile')
if (shouldRefresh === 'true') {
isRefreshing.value = true
sessionStorage.removeItem('refreshProfile')
// 延迟刷新,给用户一个视觉反馈
setTimeout(() => {
window.location.reload()
}, 100)
}
// 如果当前在消息标签页,自动加载消息数量统计
if (activeTab.value === 'message') {
loadMessageCounts()
}
})
</script>
<style scoped>
/* 刷新遮罩样式 */
.refresh-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
backdrop-filter: blur(4px);
}
.refresh-content {
text-align: center;
color: #333;
}
.refresh-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 16px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 基础布局类 */
.flex-col {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.justify-between {
justify-content: space-between;
}
/* 主页面容器 */
.profile-page {
width: 100vw;
min-height: calc(100vh - 6.4vh);
/* 减去顶部导航栏高度 */
background: #f6f6f6;
padding: 0;
display: flex;
justify-content: center;
}
/* 主要内容区域 */
.profile-content {
/* width: 80vw; */
/* 调整为80vw */
min-height: 1415px;
margin: 3vh auto 0 auto;
/* 距离顶部40px转换为vh */
position: relative;
background: #f6f6f6;
}
/* 左侧侧边栏 */
.block_14 {
position: relative;
width: 13.4vw;
height: auto;
background: #ffffff;
/* 改为白色背景 */
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30px;
/* 内容居中 */
}
/* 用户头像和姓名 */
.image_7 {
width: 5vw !important;
/* 调整头像大小覆盖SafeAvatar的内联样式 */
height: 5vw !important;
border-radius: 50%;
margin: 2.6vh auto 0 auto;
/* 居中显示 */
}
.image_7 img {
object-fit: cover;
display: block;
}
.text_72 {
width: 100%;
height: auto;
font-size: 1.1vw;
/* 调整字体大小 */
color: rgba(0, 0, 0, 1);
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: center;
line-height: 1.6vh;
margin: 0.9vh 0 0 0;
/* 居中显示 */
}
/* 菜单背景 */
.box_22 {
width: 12vw;
/* 调整菜单背景宽度,适应缩小的导航栏 */
height: auto;
/* 自适应高度 */
background: transparent;
/* 去掉背景色 */
border-radius: 0.6vw;
/* 12px转换为vw */
margin: 2.55vh 0;
/* 去掉左右边距,因为父容器已经居中 */
padding: 1.04vh 0;
/* 20px 0转换 */
display: flex;
flex-direction: column;
align-items: center;
/* 菜单项居中 */
}
/* 分割线 */
.menu-divider {
width: 11vw;
/* 与菜单项宽度一致 */
height: 0.05vh;
/* 1px转换为vh */
background: #E6E6E6;
margin: 0;
/* 上下间距 */
}
/* 菜单项 */
.image-text_19,
.image-text_20,
.image-text_21,
.image-text_22,
.image-text_23,
.image-text_24,
.image-text_25,
.image-text_26,
.image-text_27 {
width: 11vw;
/* 进一步缩小菜单项宽度,适应新的导航栏宽度 */
height: auto;
/* 自适应高度 */
min-height: 3vh;
/* 设置最小高度,让盒子更大 */
margin: .5vh;
/* 减小间距从2.34vh减少到1.5vh */
display: flex;
align-items: center;
justify-content: left;
/* 菜单项内容居中 */
cursor: pointer;
padding: 1.5vh 0 1.5vh 1.8vw;
/* 增加上下内边距,让盒子更高 */
border-radius: 0.31vw;
/* 6px转换为vw */
transition: all 0.3s ease;
background: transparent;
/* 默认透明背景 */
}
.image-text_19:hover,
.image-text_20:hover,
.image-text_21:hover,
.image-text_22:hover,
.image-text_23:hover,
.image-text_24:hover,
.image-text_25:hover,
.image-text_26:hover,
.image-text_27:hover {
background: #eff7fc;
/* 悬停时背景色 */
}
/* 激活状态的菜单项 */
.image-text_19.active,
.image-text_20.active,
.image-text_21.active,
.image-text_22.active,
.image-text_23.active,
.image-text_24.active,
.image-text_25.active,
.image-text_26.active,
.image-text_27.active {
background: #eff7fc;
/* 激活时背景色 */
}
/* 菜单图标 */
.image_8,
.label_4,
.label_5,
.label_6,
.thumbnail_40,
.label_7,
.image_9,
.thumbnail_41,
.thumbnail_42 {
width: 1.04vw;
/* 20px转换为vw */
height: 1.04vw;
margin-right: 0.78vw;
/* 15px转换为vw */
}
/* 菜单文字 */
.text-group_19,
.text-group_20,
.text-group_21,
.text-group_22,
.text-group_23,
.text-group_24,
.text-group_25,
.text-group_26,
.text-group_27 {
font-size: 16px;
/* 20px转换为vw */
color: rgba(102, 102, 102, 1);
/* 默认灰色 */
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
line-height: 1.46vh;
/* 28px转换为vh */
transition: color 0.3s ease;
}
/* 激活状态的菜单文字颜色 */
.image-text_19.active .text-group_19,
.image-text_20.active .text-group_20,
.image-text_21.active .text-group_21,
.image-text_22.active .text-group_22,
.image-text_23.active .text-group_23,
.image-text_24.active .text-group_24,
.image-text_25.active .text-group_25,
.image-text_27.active .text-group_27,
.image-text_26.active .text-group_26 {
color: rgba(2, 134, 206, 1);
/* 激活时蓝色 */
}
/* 悬停状态的菜单文字颜色 */
.image-text_19:hover .text-group_19,
.image-text_20:hover .text-group_20,
.image-text_21:hover .text-group_21,
.image-text_22:hover .text-group_22,
.image-text_23:hover .text-group_23,
.image-text_24:hover .text-group_24,
.image-text_25:hover .text-group_25,
.image-text_27:hover .text-group_27,
.image-text_26:hover .text-group_26 {
color: rgba(2, 134, 206, 1);
/* 悬停时蓝色 */
}
/* 菜单项图标悬停效果 */
.image-text_19 .hover-icon,
.image-text_20 .hover-icon,
.image-text_21 .hover-icon,
.image-text_22 .hover-icon,
.image-text_23 .hover-icon,
.image-text_24 .hover-icon,
.image-text_25 .hover-icon,
.image-text_26 .hover-icon,
.image-text_27 .hover-icon {
display: none;
}
.image-text_19:hover .default-icon,
.image-text_20:hover .default-icon,
.image-text_21:hover .default-icon,
.image-text_22:hover .default-icon,
.image-text_23:hover .default-icon,
.image-text_24:hover .default-icon,
.image-text_25:hover .default-icon,
.image-text_26:hover .default-icon,
.image-text_27:hover .default-icon {
display: none;
}
.image-text_19:hover .hover-icon,
.image-text_20:hover .hover-icon,
.image-text_21:hover .hover-icon,
.image-text_22:hover .hover-icon,
.image-text_23:hover .hover-icon,
.image-text_24:hover .hover-icon,
.image-text_25:hover .hover-icon,
.image-text_26:hover .hover-icon,
.image-text_27:hover .hover-icon {
display: block;
}
/* 激活状态的图标显示 */
.image-text_19.active .default-icon,
.image-text_20.active .default-icon,
.image-text_21.active .default-icon,
.image-text_22.active .default-icon,
.image-text_23.active .default-icon,
.image-text_24.active .default-icon,
.image-text_25.active .default-icon,
.image-text_26.active .default-icon,
.image-text_27.active .default-icon {
display: none;
}
.image-text_19.active .hover-icon,
.image-text_20.active .hover-icon,
.image-text_21.active .hover-icon,
.image-text_22.active .hover-icon,
.image-text_23.active .hover-icon,
.image-text_24.active .hover-icon,
.image-text_25.active .hover-icon,
.image-text_26.active .hover-icon,
.image-text_27.active .hover-icon {
display: block;
}
/* 右侧课程列表区域 */
.group_5 {
width: 65vw;
/* 保持60vw宽度 */
min-height: calc(100vh - 6.4vh);
padding: 2.08vh 1.9vw;
/* 40px转换为vh和vw */
background: rgba(255, 255, 255, 1);
overflow-y: auto;
margin-left: 1.56vw;
margin-bottom: 30px;
}
/* 课程筛选标签 */
.text-wrapper_1 {
width: 100%;
height: 2.08vh;
/* 40px转换为vh */
align-items: center;
margin: 20px 0 20px 0;
/* 32px转换为vh */
gap: 2.81vw;
/* 54px转换为vw54/1920*100 = 2.81vw */
}
.text_12,
.text_13,
.text_14,
.text_15 {
font-size: 0.94vw;
/* 18px转换为vw */
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,
.text_15.active {
color: rgba(2, 134, 206, 1);
/* 只改变字体颜色,去掉背景色 */
}
.text_12:hover,
.text_13:hover,
.text_14:hover,
.text_15:hover {
color: rgba(2, 134, 206, 1);
}
/* 课程区域分割线 */
.course-divider {
width: 100%;
/* 与课程卡片宽度一致 */
height: 1.5px;
/* 1px转换为vh */
background: #E6E6E6;
margin-bottom: 1.67vh;
/* 与课程列表的间距 */
}
/* 课程列表 */
.course-list {
display: flex;
flex-direction: column;
gap: 1.25vh;
/* 24px转换为vh */
}
/* 课程卡片 */
.box_2 {
width: 100%;
min-height: 10.42vh;
/* 200px转换为vh */
background: rgba(255, 255, 255, 1);
border: none;
/* 去掉边框 */
border-radius: 0.6vw;
/* 12px转换为vw */
padding: 1.04vh 1.04vw;
/* 20px转换 */
transition: all 0.3s ease;
}
.box_2:hover {
box-shadow: 0 0.21vh 1.04vh rgba(0, 0, 0, 0.1);
/* 0 4px 20px转换 */
transform: translateY(-0.1vh);
/* -2px转换为vh */
}
/* 课程缩略图区域 */
.block_4 {
margin-right: 1.04vw;
}
.box_3 {
width: 202px;
/* 200px转换为vw */
height: 156px;
/* 大幅增加高度到300px转换为vh让图片展示到外部盒子高度 */
position: relative;
border-radius: 5px;
/* 8px转换为vw */
overflow: hidden;
}
.thumbnail_4 {
width: 100%;
height: 156px;
object-fit: cover;
}
/* 状态标签 */
.status-image-container {
position: absolute;
top: 0;
left: 0;
z-index: 10;
}
.status-image {
width: 66px;
height: 22px;
}
.status-text {
position: absolute;
top: 26vh;
/* 12px转换为vh */
right: 0.63vw;
/* 调整到右侧 */
padding: 0.21vh 0.63vw;
/* 4px 12px转换 */
border-radius: 0.21vw;
/* 4px转换为vw */
font-size: 0.63vw;
/* 12px转换为vw */
color: rgba(255, 255, 255, 1);
font-weight: 500;
}
.status-text.learning {
background: rgba(82, 196, 26, 1);
}
.status-text.completed {
background: rgba(24, 144, 255, 1);
}
/* 课程信息区域 */
.block_5 {
flex: 1;
display: flex;
flex-direction: column;
}
/* 课程标题和评分 */
.group_6 {
align-items: center;
margin-bottom: 0.42vh;
/* 8px转换为vh */
gap: 0.63vw;
/* 12px转换为vw */
}
.text_16 {
font-size: 18px;
/* 18px转换为vw */
color: #000000;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: 600;
flex: 1;
}
.thumbnail_5 {
width: 0.83vw;
/* 16px转换为vw */
height: 0.83vw;
}
.text_17 {
font-size: 0.73vw;
/* 14px转换为vw */
color: #999999;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
}
/* 讲师信息 */
.text_18 {
font-size: 14px;
/* 14px转换为vw */
color: #999;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
margin-bottom: 0.42vh;
/* 8px转换为vh */
}
/* 课程描述 */
.text_19 {
font-size: 14px;
/* 14px转换为vw */
color: #999;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
line-height: 1.5;
margin-top: 5px;
margin-bottom: 22px;
/* 16px转换为vh */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 课程统计信息 */
.group_7 {
align-items: center;
gap: 5px;
/* 20px转换为vw */
}
.thumbnail_6,
.thumbnail_7 {
width: 14px;
height: 14px;
}
.thumbnail_8 {
width: 18px;
height: 18px;
}
.text_20,
.text_21,
.text_22 {
font-size: 14px;
/* 14px转换为vw */
color: #999;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
margin-right: 10px;
}
/* 操作按钮 */
.text-wrapper_2 {
background: #0288D1;
border-radius: 5px;
/* 6px转换为vw */
padding: 6px 22px;
/* 8px 16px转换 */
cursor: pointer;
transition: background-color 0.3s ease;
margin-left: auto;
}
.text-wrapper_2:hover {
background: rgba(2, 134, 206, 0.8);
}
.text_23 {
font-size: 14px;
/* 14px转换为vw */
color: white;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: center;
}
/* 分页器样式 */
.pagination-wrapper {
width: 100%;
display: flex;
justify-content: center;
margin-top: 4.17vh;
/* 80px转换为vh增加上方间距 */
padding: 1.04vh 0;
/* 20px转换为vh */
}
.pagination {
display: flex;
align-items: center;
gap: 0.52vw;
/* 10px转换为vw */
}
.pagination-item {
min-width: 38px;
height: 38px;
background: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.73vw;
/* 14px转换为vw */
color: #666666;
font-family: 'Microsoft YaHei', Arial, sans-serif;
cursor: pointer;
border-radius: 0.21vw;
/* 4px转换为vw */
transition: all 0.3s ease;
padding: 0.52vh 0.83vw;
/* 10px 16px转换 */
border: 0.05vw solid rgba(232, 232, 232, 1);
}
.pagination-item:hover {
color: rgba(2, 134, 206, 1);
border-color: rgba(2, 134, 206, 1);
}
.pagination-item.active {
background: #0088D1;
color: rgba(255, 255, 255, 1);
border-color: #0088D1;
}
.pagination-item.disabled {
color: rgba(204, 204, 204, 1);
cursor: not-allowed;
border-color: rgba(232, 232, 232, 1);
}
.pagination-item.disabled:hover {
color: rgba(204, 204, 204, 1);
border-color: rgba(232, 232, 232, 1);
background: rgba(248, 248, 248, 1);
}
.nav-button {
min-width: 3.65vw;
border: none;
}
.page-number {
min-width: 2.08vw;
/* 40px转换为vw页码按钮保持原宽度 */
}
/* 作业部分样式 - 严格按照蓝湖UI */
.homework-content {
width: 100%;
}
/* 作业卡片主容器 */
.group_11 {
padding-top: 15px;
position: relative;
width: 100%;
/* 1033px转换为vw */
min-height: 25vh;
/* 设置最小高度 */
height: auto;
/* 改为自适应高度 */
/* background: url('https://lanhu-oss-2537-2.lanhuapp.com/SketchPng75139e59843c2accc2ef713eac90adeeea2be065395a76d5e5e3d7365677e090') -0.05vw -0.05vh no-repeat; */
background-size: 100% auto;
/* 背景图宽度100%,高度自适应 */
background-color: #fff;
/* 添加背景色,避免内容超出背景图范围时显示问题 */
margin: 0.2vh 0 3vh 0;
/* 减少底部间距,避免卡片挤在一起 */
/* padding-top: 3vh;
padding-bottom: 3vh; */
/* 增加内边距确保内容不会溢出 */
display: flex;
flex-direction: column;
border: 2px solid #ECECEC;
}
/* 头部信息容器 */
.box_5 {
/* width: 51.46vw; */
/* 988px转换为vw */
height: 2.81vh;
/* 54px转换为vh */
margin: 1.15vh 0 0 1.2vw;
/* 22px 0 0 23px转换 */
display: flex;
justify-content: space-between;
align-items: center;
}
/* 教师信息 */
.image-text_2 {
width: 8.07vw;
/* 155px转换为vw */
height: 2.81vh;
/* 54px转换为vh */
display: flex;
align-items: center;
margin-top: 3.3vh;
/* 25px转换为vh (25/1920*100) */
position: relative;
gap: 8px;
}
.avatar-line {
position: absolute;
left: -4px;
top: 53%;
transform: translateY(-50%);
width: 57px;
height: 56px;
border-radius: 50%;
border: 2px solid #0288D1;
}
/* 教师头像 */
.image_22 {
width: 2.71vw;
/* 52px转换为vw */
height: 2.71vw;
/* 改为vw单位保持正圆形 */
border-radius: 50%;
object-fit: cover;
margin-top: 2vh;
/* 头像向下移动 */
}
.image-avatar {
width: 2.71vw;
/* 52px转换为vw */
height: 2.71vw;
border: 2px solid #000;
background-color: pink;
}
/* 教师详细信息容器 */
.text-group_3 {
width: 4.43vw;
/* 85px转换为vw */
height: auto;
/* 改为自适应高度 */
display: flex;
flex-direction: column;
justify-content: space-between;
/* 增加垂直间距 */
margin-left: 0.52vw;
/* 10px转换为vw头像和姓名之间的间距 */
margin-top: -2vh;
/* 25px转换为vh (25/1920*100) */
}
/* 教师姓名 */
.text_30 {
width: 2.81vw;
/* 54px转换为vw */
height: 1.3vh;
/* 25px转换为vh */
overflow-wrap: break-word;
color: rgba(51, 51, 51, 1);
font-size: 0.94vw;
/* 18px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 1.3vh;
/* 恢复原来的行高 */
margin-top: 0.5vh;
/* 调整姓名位置 */
}
/* 作业时间 */
.text_31 {
width: 4.43vw;
/* 85px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
overflow-wrap: break-word;
color: #999999;
font-size: 14px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 1.04vh;
/* 20px转换为vh */
margin-top: 1.2vh;
margin-top: 15px;
/* 调整时间与姓名的间距 */
}
/* 作业状态 */
.text_32 {
width: auto;
/* 42px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
overflow-wrap: break-word;
color: rgba(153, 153, 153, 1);
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 1.04vh;
/* 20px转换为vh */
margin-top: 0.47vh;
margin-right: 25px;
/* 9px转换为vh */
display: flex;
align-items: center;
}
.text_32 img {
margin-right: 7px;
width: 16px;
height: 16px;
}
/* 作业内容区域 */
.text-group_4 {
/* width: 48.02vw; */
/* 922px转换为vw */
height: auto;
/* 改为自适应高度 */
margin: 4vh 1vw 0 5vw;
/* 减少顶部margin让内容更紧凑 */
flex-grow: 1;
/* 允许内容区域伸展 */
}
/* 作业标题 */
.text_33 {
width: 16.67vw;
/* 320px转换为vw */
min-height: 1.46vh;
/* 最小高度,允许内容增长 */
height: auto;
/* 自适应高度 */
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 1.04vw;
/* 20px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: normal;
/* 允许文本换行 */
line-height: 1.8vh;
/* 增加行高,提高可读性 */
padding-top: 10px;
padding-bottom: 5px;
margin-bottom: 1vh;
/* 添加底部间距 */
display: block;
/* 确保是块级元素 */
}
/* 作业描述容器 */
.description-container {
/* width: 48.02vw; */
/* 922px转换为vw */
position: relative;
}
/* 作业描述 */
.text_34 {
width: 100%;
min-height: 1.15vh;
/* 最小高度 */
height: auto;
/* 自适应高度 */
overflow-wrap: break-word;
color: rgba(102, 102, 102, 1);
font-size: 0.83vw;
/* 16px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: normal;
/* 允许文本换行 */
line-height: 1.8vh;
/* 增加行高,提高可读性 */
display: block;
/* 确保是块级元素 */
margin-top: 0;
/* 移除顶部间距,因为已经在标题添加了底部间距 */
transition: all 0.3s ease;
/* 添加过渡动画 */
}
/* 文本截断样式 */
.text_34.text-truncated {
/* display: -webkit-box; */
-webkit-line-clamp: 1;
line-height: 1.4;
/* 限制显示1行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
/* 展开/收起按钮 */
.expand-toggle {
color: #1890ff;
cursor: pointer;
font-size: 0.73vw;
/* 14px转换为vw */
margin-left: 0.5vw;
user-select: none;
transition: color 0.3s ease;
}
.expand-toggle:hover {
color: #40a9ff;
text-decoration: underline;
}
/* 作业详情弹窗样式 */
.assignment-detail {
padding: 20px 0;
}
.detail-header {
/* padding-bottom: 20px; */
}
.detail-header h2 {
margin: 0 0 15px 0;
color: #333;
font-size: 24px;
font-weight: 600;
}
.assignment-meta {
display: flex;
flex-direction: column;
gap: 10px;
}
.teacher-info {
display: flex;
align-items: center;
gap: 10px;
}
.teacher-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.teacher-name {
font-weight: 500;
color: #333;
}
.assignment-time {
color: #666;
font-size: 14px;
}
.assignment-status {
font-size: 14px;
font-weight: 500;
}
.assignment-status.未完成,
.assignment-status.待提交 {
color: #ff4d4f;
}
.assignment-status.已完成,
.assignment-status.已完成 {
color: #52c41a;
}
.detail-content h3 {
margin: 20px 0 10px 0;
color: #333;
font-size: 18px;
font-weight: 600;
}
.description-full {
background: #f8f9fa;
padding: 15px;
border-radius: 6px;
line-height: 1.6;
color: #333;
white-space: pre-wrap;
word-wrap: break-word;
}
.attachments-section {
margin-top: 20px;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.attachment-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #e9ecef;
}
.attachment-icon {
width: 24px;
height: 24px;
object-fit: cover;
}
.attachment-name {
color: #333;
font-size: 14px;
}
/* 附件区域 */
.box_6 {
/* width: 48.65vw; */
/* 934px转换为vw */
height: auto;
/* 改为自适应高度 */
margin: 0.94vh 0 0 5vw;
/* 18px 0 0 67px转换 */
position: relative;
display: flex;
flex-direction: column;
}
/* 附件小图标 */
.thumbnail_6 {
width: 0.83vw;
/* 16px转换为vw */
height: 0.83vw;
/* 保持宽高比一致 */
}
/* 附件大图标 */
.image_23 {
width: 0.83vw;
/* 16px转换为vw */
height: 2.03vh;
/* 39px转换为vh */
margin: 6.51vh 0 0 0.94vw;
/* 125px 0 0 18px转换 */
}
/* 附件数量文本 */
.text_35 {
width: 4.27vw;
/* 82px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
overflow-wrap: break-word;
color: rgba(153, 153, 153, 1);
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 1.04vh;
/* 20px转换为vh */
margin: 7.76vh 0 0 0.31vw;
/* 149px 0 0 6px转换 */
position: absolute;
}
/* 附件头部区域 */
.attachment-header {
position: relative;
height: 3vh;
margin-bottom: 1vh;
}
/* 附件图片容器 */
.attachment-images {
display: flex;
gap: 0.7vw;
align-items: center;
margin-top: 1vh;
/* margin-left: 2.5vh; */
}
.attachment-number-container {
justify-content: left;
gap: 0;
align-items: center;
}
.attachment-number-icon {
width: 14px;
height: 12px;
}
.attachment-number-text {
margin-left: 5px;
font-size: 14px;
color: #999;
}
/* 附件图片样式 - 使用flex布局避免重叠 */
.image_24,
.image_25,
.image_26,
.image_27,
.image_28 {
width: 6.5vw;
/* 缩小图片尺寸 */
height: 6.5vw;
/* 保持正方形 */
object-fit: cover;
border: 0.05vw solid #e9ecef;
}
/* 作业按钮区域 */
.assignment-buttons {
display: flex;
gap: 1vw;
margin: 2vh 0 2vh 4.95vw;
}
/* 提交按钮容器 */
.text-wrapper_8 {
height: 1.72vh;
/* 33px转换为vh */
width: 5.31vw;
/* 102px转换为vw */
position: relative;
/* 改为相对定位 */
cursor: pointer;
align-self: flex-start;
/* 按钮靠左对齐 */
border-radius: 0.2vw;
display: flex;
align-items: center;
justify-content: center;
background-color: #0288D1;
color: #fff;
}
/* 上传作业按钮 - 未完成状态 */
.submit-button {
width: 102px;
height: 33px;
border: 1px solid #0288D1;
}
.anew-button {
width: 102px;
height: 33px;
background-color: #fff;
color: #0288D1 !important;
border: 1px solid #0288D1;
font-size: 14px;
}
/* 查看情况按钮 - 未完成状态 */
.view-button {
width: 102px;
height: 33px;
background-color: white;
border: 1px solid #0288D1;
color: #0288D1;
font-size: 14px;
}
/* 查看详情按钮 - 已完成状态 */
.details-button {
width: 102px;
height: 33px;
background-color: #0288D1;
color: white !important;
}
/* 提交按钮文字 */
.text_36 {
width: auto;
/* 自适应宽度 */
height: 1.04vh;
/* 20px转换为vh */
overflow-wrap: break-word;
color: #fff;
font-size: 14px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 1.04vh;
margin: 0;
}
.text-view {
color: #0288D1;
}
/* 主图样式 */
.image_29 {
position: absolute;
left: 4.95vw;
/* 95px转换为vw */
top: 12vh;
/* 调整位置,避免与附件重叠 */
width: 6.98vw;
/* 134px转换为vw */
height: 6.98vh;
/* 134px转换为vh */
object-fit: cover;
border-radius: 0.3vw;
border: 0.05vw solid #e9ecef;
}
/* 蓝湖UI作业样式完成 */
/* 考试页面样式 - 网格布局 */
.exam-content {
width: 100%;
}
.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;
}
.exam-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: #999;
font-weight: 500;
}
.exam-description {
padding: 8px 10px;
font-size: 12px;
color: #497087;
margin-bottom: auto;
flex: 1;
overflow: hidden;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
background-color: #F5F8FB;
}
.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;
}
.upcoming-btn {
background: #F5F8FB;
color: #999999;
border: none;
}
.upcoming-btn:hover {
background: #e8e8e8;
}
.ongoing-btn {
background: #0288D1;
color: white;
}
.ongoing-btn:hover {
background: #40a9ff;
}
.finished-btn {
background: #0288D1;
color: white;
}
.finished-btn:hover {
background: #01579B;
}
.files-container {
margin: 15px 20px 0 83px;
/* width: 100%; */
background-color: #F5F9FC;
height: 48px;
display: flex;
align-items: center;
}
.file-items {
display: flex;
align-items: center;
padding: 10px;
cursor: pointer;
gap: 4px;
}
.file-items span {
font-size: 12px;
}
.files-icon {
width: 15px;
height: 15px;
}
.course-name {
margin-left: 0px;
color: #497087;
}
.course-name span {
color: #6AA5CC;
}
/* 响应式设计 */
@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;
}
/* 分数徽章保持在右上角不换行 */
.exam-score-badge {
position: absolute;
top: 16px;
right: 16px;
white-space: nowrap;
}
}
/* 更小屏幕的适配 */
@media (max-width: 480px) {
.exam-card {
padding: 12px;
min-height: 220px;
}
.exam-title {
padding-right: 70px;
/* 给分数徽章留出更多空间 */
font-size: 14px;
}
.exam-score-badge {
top: 12px;
right: 12px;
}
.score-text {
font-size: 16px;
}
}
/* 简化的作业卡片样式 */
.assignment-card-simple {
width: 100%;
margin-bottom: 2vh;
}
/* 其他内容样式 */
.other-content {
width: 100%;
padding: 2.08vh 2.08vw;
/* 40px转换 */
text-align: center;
}
.other-content h2 {
font-size: 1.25vw;
/* 24px转换为vw */
color: rgba(51, 51, 51, 1);
margin-bottom: 1.04vh;
/* 20px转换为vh */
}
.other-content p {
font-size: 0.83vw;
/* 16px转换为vw */
color: rgba(153, 153, 153, 1);
}
/* 作业分隔线 */
.assignment-divider {
width: 100%;
height: 1px;
background-color: #E5E5E5;
margin: 0.89vh 0;
/* 17px转换为vh */
}
/* 响应式设计 */
@media (max-width: 1440px) {
.profile-page {
max-width: 100vw;
}
.profile-content {
max-width: 100vw;
}
}
@media (max-width: 1024px) {
.profile-content {
flex-direction: column;
}
.block_14 {
width: 100%;
min-height: auto;
order: 2;
border-right: none;
border-top: 0.05vw solid rgba(232, 232, 232, 1);
}
.group_5 {
width: 100%;
order: 1;
min-height: auto;
margin-left: 0;
/* 移动端取消左边距 */
}
.box_2 {
flex-direction: column;
}
.block_4 {
width: 100%;
margin-right: 0;
margin-bottom: 0.83vh;
/* 16px转换为vh */
}
.box_3 {
width: 100%;
height: 18vh;
/* 平板端大幅增加高度,让图片展示到外部盒子高度 */
}
/* 调整用户信息区域为横向布局 */
.image_7 {
margin: 1.04vh 0 0 1.04vw;
/* 20px转换 */
width: 4.17vw !important;
/* 80px转换为vw覆盖SafeAvatar样式 */
height: 4.17vw !important;
}
.text_72 {
margin: 1.04vh 0 0 1.04vw;
/* 20px转换 */
width: auto;
text-align: left;
}
.box_22 {
margin: 1.04vh 0;
/* 移动端保持居中 */
width: 90%;
/* 移动端调整宽度 */
height: auto;
padding: 1.04vh 0;
/* 移动端调整内边距 */
}
.image-text_19,
.image-text_20,
.image-text_21,
.image-text_22,
.image-text_23,
.image-text_24,
.image-text_25,
.image-text_26 {
width: 80%;
/* 移动端菜单项宽度 */
margin: 1.5vh 0;
/* 保持与桌面端一致的间距 */
padding: 1.2vh 2vw;
/* 保持与桌面端一致的内边距 */
min-height: 3vh;
/* 保持最小高度 */
}
.menu-divider {
width: 80%;
/* 移动端分割线宽度 */
margin: 1.5vh 0;
}
}
@media (max-width: 768px) {
.group_5 {
padding: 1.04vh 1.04vw;
/* 20px转换 */
}
.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%;
/* 手机端也保持与课程卡片宽度一致 */
}
.pagination-wrapper {
margin-top: 6vh;
/* 手机端增加更多上方间距 */
}
.pagination {
gap: 2vw;
/* 手机端增加间距 */
}
.pagination-item {
min-width: 8vw;
/* 手机端增加按钮宽度 */
height: 4vh;
/* 手机端增加按钮高度 */
font-size: 2.5vw;
/* 手机端调整字体大小 */
padding: 1vh 2vw;
}
.nav-button {
min-width: 12vw;
/* 手机端导航按钮更宽 */
}
.page-number {
min-width: 8vw;
/* 手机端页码按钮宽度 */
}
/* 作业部分响应式 */
.assignment-card {
width: 90vw;
height: auto;
min-height: 30vh;
margin: 2vh 5vw;
background-size: 100% 100%;
}
.assignment-header {
width: 85vw;
height: auto;
margin: 2vh 2.5vw;
flex-direction: column;
align-items: flex-start;
gap: 1vh;
}
.teacher-info {
width: auto;
height: auto;
gap: 2vw;
}
.teacher-avatar {
width: 8vw;
height: 8vw;
}
.teacher-details {
width: auto;
height: auto;
}
.teacher-name {
font-size: 3vw;
width: auto;
height: auto;
}
.assignment-time {
font-size: 2.5vw;
width: auto;
height: auto;
margin-top: 0.5vh;
}
.assignment-status {
font-size: 2.5vw;
width: auto;
height: auto;
margin-top: 0;
}
.assignment-content {
width: 85vw;
height: auto;
margin: 2vh 2.5vw;
}
.assignment-title {
font-size: 3.5vw;
width: auto;
height: auto;
white-space: normal;
line-height: 1.4;
}
.assignment-description {
font-size: 2.8vw;
width: auto;
height: auto;
white-space: normal;
line-height: 1.4;
margin-top: 1vh;
}
.assignment-attachments {
width: 85vw;
height: auto;
margin: 2vh 2.5vw;
flex-direction: column;
align-items: flex-start;
gap: 2vh;
}
.attachment-list {
flex-wrap: wrap;
gap: 2vw;
}
.attachment-item {
width: 15vw;
height: 15vw;
}
.attachment-count {
font-size: 2.5vw;
width: auto;
height: auto;
margin-left: 0;
}
.attachment-actions {
width: auto;
height: auto;
gap: 2vw;
margin-bottom: 0;
}
.action-btn {
width: 20vw;
height: 6vh;
border-radius: 1vw;
}
.download-btn span,
.submit-btn span {
font-size: 2.5vw;
width: auto;
height: auto;
margin: 0;
}
.assignment-main-image {
position: relative;
left: 2.5vw;
top: 0;
width: 20vw;
height: 20vw;
margin-top: 2vh;
}
.other-content {
padding: 4vh 4vw;
}
.other-content h2 {
font-size: 4vw;
margin-bottom: 2vh;
}
.other-content p {
font-size: 3vw;
}
.group_7 {
flex-direction: column;
align-items: flex-start;
gap: 0.42vh;
/* 8px转换为vh */
}
.box_2 {
padding: 0.83vh 0.83vw;
/* 16px转换 */
}
.block_4 {
margin-bottom: 0.63vh;
/* 12px转换为vh */
}
.box_3 {
height: 20vh;
/* 手机端大幅增加高度,让图片展示到外部盒子高度 */
}
}
@media (max-width: 576px) {
.profile-page {
padding: 0;
}
.group_5 {
padding: 0.83vh 0.83vw;
/* 16px转换 */
}
.text-wrapper_1 {
gap: 0.83vw;
/* 16px转换为vw */
}
.text_12,
.text_13,
.text_14 {
font-size: 0.83vw;
/* 16px转换为vw */
padding: 0.31vh 0.63vw;
/* 6px 12px转换 */
}
/* 平板端面包屑样式 */
.breadcrumb-nav {
font-size: 1vw;
/* 平板端调整字体大小 */
}
.breadcrumb-item {
width: 5vw;
/* 平板端调整宽度 */
font-size: 1vw;
/* 平板端调整字体大小 */
line-height: 1.3vh;
/* 平板端调整行高 */
}
.breadcrumb-current {
width: 4.5vw;
/* 平板端调整宽度 */
font-size: 1vw;
/* 平板端调整字体大小 */
line-height: 1.3vh;
/* 平板端调整行高 */
}
.breadcrumb-separator {
margin: 0 0.8vw;
/* 平板端调整间距 */
}
.box_2 {
padding: 0.63vh 0.63vw;
/* 12px转换 */
}
.text_16 {
font-size: 0.83vw;
}
.text_19 {
font-size: 0.68vw;
/* 13px转换为vw */
}
.group_7 {
gap: 0.31vh;
/* 6px转换为vh */
}
.text_20,
.text_21,
.text_22 {
font-size: 0.63vw;
/* 12px转换为vw */
}
/* 手机端面包屑样式 */
.breadcrumb-nav {
font-size: 3.5vw;
/* 手机端调整字体大小 */
margin-bottom: 2vh;
/* 手机端增加底部间距 */
}
.breadcrumb-item {
width: 15vw;
/* 手机端调整宽度 */
font-size: 3.5vw;
/* 手机端调整字体大小 */
line-height: 2vh;
/* 手机端调整行高 */
}
.breadcrumb-current {
width: 13vw;
/* 手机端调整宽度 */
font-size: 3.5vw;
/* 手机端调整字体大小 */
line-height: 2vh;
/* 手机端调整行高 */
}
.breadcrumb-separator {
margin: 0 2vw;
/* 手机端调整间距 */
}
}
/* 上传作业弹窗样式 */
.assignment-info {
margin-bottom: 20px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 8px;
}
.assignment-info h3 {
margin-top: 0;
margin-bottom: 10px;
font-size: 18px;
color: #333;
}
.assignment-info p {
margin: 0;
font-size: 14px;
color: #666;
line-height: 1.5;
}
.file-list {
margin-top: 10px;
max-height: 200px;
overflow-y: auto;
border: 1px solid #eee;
border-radius: 4px;
}
.file-item {
display: flex;
justify-content: space-between;
padding: 8px 12px;
border-bottom: 1px solid #eee;
}
.file-item:last-child {
border-bottom: none;
}
.modal-footer {
width: 100%;
display: flex;
justify-content: flex-start;
margin-top: -30px;
}
.modal-footer button {
width: 100px;
height: 42px;
background: #0088D1;
border: none;
color: white;
border-radius: 5px;
font-size: 16px;
}
.cancel {
background: #E2F5FF !important;
color: #0088D1 !important;
border: 1px solid #0088D1 !important;
}
/* 作业详情视图样式 */
.detail-header {
display: flex;
align-items: center;
/* margin-bottom: 15px; */
}
/* 面包屑导航样式 */
.breadcrumb-nav {
display: flex;
align-items: center;
font-size: 0.73vw;
/* 14px转换为vw */
color: #666;
margin-bottom: 1.04vh;
/* 20px转换为vh */
}
.breadcrumb-item {
width: 3.39vw;
/* 65px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 0.73vw;
/* 14px转换为vw */
color: #333333;
line-height: 1.04vh;
/* 20px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
transition: color 0.3s ease;
}
.breadcrumb-item:hover {
color: #0056b3;
text-decoration: underline;
}
.breadcrumb-separator {
margin: 0 0.52vw;
/* 10px转换为vw */
color: #999;
}
.breadcrumb-current {
width: 2.97vw;
/* 57px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 0.73vw;
/* 14px转换为vw */
color: #999999;
line-height: 1.04vh;
/* 20px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
}
.description-full-view {
white-space: pre-wrap;
line-height: 1.6;
color: #666;
font-size: 14px;
}
/* 考试详情页面样式 */
.exam-detail-overlay {
position: fixed;
top: 60px;
/* 留出导航栏的空间 */
left: 0;
width: 100%;
height: calc(100% - 60px);
/* 减去导航栏的高度 */
background: #f5f5f5;
z-index: 999;
/* 降低z-index确保导航栏可见 */
overflow-y: auto;
}
.exam-detail-container {
margin: 0 auto;
background: #f5f5f5;
min-height: 100vh;
}
.exam-detail-header {
margin: auto;
width: 1420px;
padding: 24px 0;
background: #f5f5f5;
}
.breadcrumb {
font-size: 14px;
color: #666;
white-space: nowrap;
overflow: hidden;
display: flex;
align-items: center;
}
.breadcrumb-link {
height: 20px;
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
color: #333333;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
flex-shrink: 0;
}
.breadcrumb-link:hover {
text-decoration: underline;
}
.breadcrumb-separator {
margin: 0 8px;
color: #999999;
flex-shrink: 0;
}
.breadcrumb-current {
height: 20px;
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
min-width: 0;
}
.exam-detail-content {
margin: auto;
max-width: 1420px;
display: flex;
gap: 24px;
padding: 0;
background: #f5f5f5;
}
.exam-questions-section {
flex: 1;
background: white;
padding: 24px;
}
.exam-title-section {
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #e8e8e8;
}
.exam-main-title {
font-size: 20px;
font-weight: 600;
color: #333;
margin: 0;
}
.questions-container {
margin-bottom: 24px;
}
.question-item {
background: white;
padding: 20px;
margin-bottom: 20px;
}
.question-header {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.question-number {
color: #0088D1;
font-size: 14px;
font-weight: 500;
margin-right: 12px;
}
.question-type {
color: #1890ff;
font-size: 14px;
font-weight: 500;
margin-right: 12px;
}
.question-score {
width: 36px;
height: 20px;
background: #EEF9FF;
color: #1890ff;
font-size: 12px;
font-weight: 500;
margin-left: auto;
display: flex;
align-items: center;
justify-content: center;
border-radius: 25%;
}
.question-content {
font-size: 16px;
color: #333;
line-height: 1.6;
margin-bottom: 20px;
}
.question-options {
margin-bottom: 20px;
}
.option-item {
display: flex;
align-items: center;
width: 1053px;
height: 50px;
background: #F5F8FB;
padding: 0 16px;
margin-bottom: 8px;
transition: all 0.3s ease;
}
.option-item.option-selected {
background: #F5F8FB !important;
}
.option-item.option-correct {
background: #f6ffed;
}
.option-item.option-wrong {
background: #fff2f0;
}
.option-checkbox {
margin-right: 12px;
flex-shrink: 0;
}
.option-checkbox input[type="checkbox"] {
width: 14px;
height: 14px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background: white;
border: 1px solid #d9d9d9;
cursor: pointer;
position: relative;
}
.option-checkbox input[type="checkbox"]:checked {
background: #1890ff;
border-color: #1890ff;
}
.option-checkbox input[type="checkbox"]:checked::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 10px;
font-weight: bold;
}
.option-content {
flex: 1;
display: flex;
align-items: center;
}
.option-text {
font-size: 14px;
color: #333;
line-height: 1.5;
}
.option-image {
margin-top: 8px;
}
.option-image img {
max-width: 200px;
height: auto;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
.question-footer {
display: flex;
align-items: center;
gap: 16px;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
}
.answer-status-box {
width: 100px;
height: 33px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
color: #FFFFFF;
line-height: 20px;
text-align: center;
font-style: normal;
text-transform: none;
}
.answer-status-box.status-correct {
background: #0288D1;
}
.answer-status-box.status-wrong {
background: #ff4d4f;
}
.question-stats {
display: flex;
gap: 16px;
flex: 1;
}
.correct-answer,
.user-answer {
font-size: 14px;
color: #666;
}
.question-result {
font-size: 20px;
font-weight: bold;
}
.result-correct {
color: #52c41a;
}
.result-wrong {
color: #ff4d4f;
}
.question-analysis {
margin-top: 16px;
padding: 16px;
background: #fafafa;
border-radius: 6px;
}
.analysis-title {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
}
.analysis-content {
font-size: 14px;
color: #497087;
line-height: 1.6;
}
.exam-submit-section {
margin-top: 24px;
text-align: left;
margin-left: 16px;
}
.submit-btn {
background: #1890ff;
color: white;
border: none;
border-radius: 6px;
padding: 12px 24px;
font-size: 14px;
cursor: pointer;
display: inline-block;
transition: background-color 0.3s ease;
}
.submit-btn:hover {
background: #40a9ff;
}
.submit-text {
font-weight: 500;
}
.submit-stats {
font-size: 12px;
opacity: 0.8;
}
.submit-score {
font-weight: bold;
}
.exam-info-section {
width: 300px;
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 16px;
}
.score-circle-container {
background: white;
padding: 24px;
text-align: center;
}
.score-circle {
width: 120px;
height: 120px;
border-radius: 50%;
background: conic-gradient(#1890ff 0deg 352deg, #e8e8e8 352deg 360deg);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 12px;
position: relative;
}
.score-inner {
width: 90px;
height: 90px;
border-radius: 50%;
background: white;
display: flex;
align-items: center;
justify-content: center;
}
.score-number {
font-size: 32px;
font-weight: bold;
color: #333;
}
.score-label {
font-size: 14px;
color: #666;
}
.exam-stats {
background: white;
padding: 24px;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.stat-item:last-child {
margin-bottom: 0;
}
.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;
}
.answer-stats {
background: white;
padding: 24px;
}
.stats-row {
display: flex;
gap: 12px;
}
.stat-box {
flex: 1;
text-align: center;
padding: 16px 8px;
border-radius: 6px;
}
.stat-box.correct {
background: #f6ffed;
border: 1px solid #b7eb8f;
}
.stat-box.wrong {
background: #fff2f0;
border: 1px solid #ffb3b3;
}
.stat-box.total {
background: #e6f7ff;
border: 1px solid #91d5ff;
}
.stat-number {
font-size: 24px;
font-weight: bold;
margin-bottom: 4px;
}
.stat-box.correct .stat-number {
color: #52c41a;
}
.stat-box.wrong .stat-number {
color: #ff4d4f;
}
.stat-box.total .stat-number {
color: #1890ff;
}
.stat-text {
font-size: 12px;
color: #666;
}
.answer-card {
background: white;
padding: 24px;
}
.card-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 16px;
}
.card-sections {
margin-bottom: 16px;
}
.card-section {
margin-bottom: 20px;
}
.card-section:last-child {
margin-bottom: 0;
}
.section-title {
font-size: 14px;
color: #666;
margin-bottom: 8px;
}
.answer-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
}
.answer-item {
width: 32px;
height: 32px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.answer-item.correct {
background: #1890ff;
color: white;
}
.answer-item.wrong {
background: #ff4d4f;
color: white;
}
.answer-item.unanswered {
background: #f5f5f5;
color: #999;
border: 1px solid #e8e8e8;
}
.answer-item:hover {
transform: scale(1.1);
}
.card-legend {
display: flex;
align-items: center;
gap: 16px;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 2px;
}
.legend-color.correct {
background: #1890ff;
}
.legend-color.wrong {
background: #ff4d4f;
}
.legend-text {
font-size: 12px;
color: #666;
}
.legend-checkbox {
margin-left: auto;
display: flex;
align-items: center;
gap: 6px;
}
.legend-checkbox input[type="checkbox"] {
width: 14px;
height: 14px;
}
.legend-checkbox label {
font-size: 12px;
color: #666;
cursor: pointer;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.exam-detail-content {
flex-direction: column;
}
.exam-info-section {
width: 100%;
}
.stats-row {
justify-content: center;
}
.stat-box {
max-width: 120px;
}
}
@media (max-width: 768px) {
.exam-detail-content {
padding: 16px;
}
.question-item {
padding: 16px;
}
.answer-grid {
grid-template-columns: repeat(4, 1fr);
}
.score-circle {
width: 100px;
height: 100px;
}
.score-inner {
width: 75px;
height: 75px;
}
.score-number {
font-size: 24px;
}
}
/* 练习页面样式 - 网格布局 */
.practice-content {
width: 100%;
}
.exam-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 20px;
}
.practice-card {
background: #ffffff;
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 20px;
position: relative;
min-height: 280px;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
.practice-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.practice-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 16px;
line-height: 1.4;
}
.practice-score-badge {
position: absolute;
top: 11px;
right: 18px;
background: white;
border: 1px solid #FF6F0F;
border-radius: 4px;
padding: 0 11px;
}
.practice-details {
margin-bottom: 10px;
}
.practice-meta-item {
display: flex;
margin-bottom: 8px;
font-size: 14px;
}
.practice-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;
}
.practice-stats {
display: flex;
gap: 16px;
height: 54px;
background: #F5F8FB;
align-items: center;
justify-content: space-evenly;
}
.divider {
width: 1px;
height: 20px;
background: #EBEBEB;
}
.stats-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.stats-item.correct .stats-number {
color: #1890ff;
}
.stats-item.wrong .stats-number {
color: #ff4d4f;
}
.stats-label {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.stats-number {
font-size: 14px;
font-weight: 600;
}
.practice-intro {
padding: 12px;
background: #f8f9fa;
border-radius: 6px;
margin-bottom: 16px;
}
.intro-title {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.intro-content {
padding-bottom: 10px;
font-size: 14px;
color: #666;
line-height: 1.5;
border-bottom: 1.5px solid #E6E6E6;
}
.practice-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
flex-shrink: 0;
}
.practice-status-left {
display: flex;
align-items: center;
}
.practice-status-text {
font-size: 14px;
color: #666;
}
.practice-action-right {
display: flex;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.practice-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.practice-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.practice-card {
padding: 16px;
min-height: 240px;
}
}
/* 活动页面样式 - 网格布局 */
.activity-content {
width: 100%;
}
.activity-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.04vw;
/* 20px转换为vw */
margin-top: 1.04vh;
/* 20px转换为vh */
}
.activity-card {
background: #ffffff;
border: 1px solid #e8e8e8;
/* 8px转换为vw */
padding: 0;
/* 移除内边距,让图片可以占满顶部 */
position: relative;
min-height: 14.58vh;
/* 280px转换为vh */
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);
/* 0 4px 12px转换 */
transform: translateY(-0.1vh);
/* -2px转换为vh */
}
/* 活动卡片顶部图片容器 */
.activity-card-image {
width: 100%;
height: 179px;
/* 179px转换为vh (179/1080*100) */
overflow: hidden;
/* 只有顶部圆角 */
}
/* 活动卡片图片 */
.activity-image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.activity-score-badge {
position: absolute;
top: 0.83vw;
/* 16px转换为vw */
right: 0.83vw;
/* 16px转换为vw */
background: #FF6B35;
color: white;
padding: 0.21vw 0.42vw;
/* 4px 8px转换为vw */
border-radius: 0.21vw;
/* 4px转换为vw */
font-size: 0.63vw;
/* 12px转换为vw */
font-weight: 500;
z-index: 1;
}
.activity-title {
font-size: 16px;
/* 16px转换为vw */
/* font-weight: 600; */
color: #333;
margin: 15px 0 5px 0;
/* 0 0 16px 0转换 */
line-height: 1.4;
padding: 0 1.04vw;
/* 添加左右内边距 */
font-weight: 600;
}
.activity-details {
/* 20px转换为vh */
padding: 0 1.04vw;
/* 添加左右内边距 */
}
.activity-meta-item {
margin-bottom: 10px;
/* 8px转换为vh */
font-size: 14px;
/* 13px转换为vw */
line-height: 1.4;
}
.activity-intro {
flex: 1;
margin-bottom: 1.04vh;
/* 20px转换为vh */
padding: 0 1.04vw;
/* 添加左右内边距 */
}
.intro-title {
font-size: 0.73vw;
/* 14px转换为vw */
font-weight: 600;
color: #333;
margin-bottom: 0.42vh;
/* 8px转换为vh */
}
.intro-content {
font-size: 14px;
/* 13px转换为vw */
color: #666;
line-height: 1.5;
margin-bottom: 10px;
/* 16px转换为vh */
/* display: -webkit-box; */
-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;
/* 添加内边距 */
}
.activity-status-left {
/* flex: 1; */
}
.activity-status-text {
font-size: 12px;
/* 13px转换为vw */
font-weight: 500;
}
/* 活动状态颜色 - 与练习中保持一致 */
.activity-status-text.ongoing {
color: #FF520F;
/* 进行中 - 橙色 */
}
.activity-status-text.finished {
color: #999999;
/* 已结束 - 灰色 */
}
.activity-action-right {
display: flex;
}
/* 活动已结束按钮样式 - 灰色 */
.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;
}
/* 活动响应式设计 */
@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;
}
}
/* 消息页面样式 */
.message-content {
width: 100%;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
/* 20px转换为vh */
padding: 10px 0;
border-bottom: 1.5px solid #e6e6e6;
}
.message-tabs {
display: flex;
gap: 2.08vw;
/* 40px转换为vw */
}
.message-tab-item {
font-size: 0.94vw;
/* 18px转换为vw */
color: #666;
cursor: pointer;
padding: 0.52vh 0;
/* 10px转换为vh */
position: relative;
display: flex;
align-items: center;
gap: 0.52vw;
/* 10px转换为vw */
}
.message-tab-item.active {
color: #1890ff;
font-weight: 500;
}
/* 移除蓝色下划线
.message-tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 0.1vh;
background-color: #1890ff;
}
*/
.message-count {
position: absolute;
top: 0;
right: -30%;
background: #ff4d4f;
color: white;
font-size: 0.63vw;
/* 12px转换为vw */
width: 14px;
height: 14px;
/* 2px 6px转换 */
border-radius: 50%;
/* 10px转换为vw */
/* 20px转换为vw */
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.message-actions {
display: flex;
gap: 1.04vw;
align-items: center;
/* 20px转换为vw */
}
.action-link {
/* width: 2.92vw; */
/* 56px转换为vw */
/* height: 1.04vh; */
/* 20px转换为vh */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 0.73vw;
/* 14px转换为vw */
color: #000000;
line-height: 1.04vh;
/* 20px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
text-decoration: none;
display: inline-flex;
align-items: center;
/* 使用flexbox确保垂直居中 */
}
.action-link:hover {
color: #000000;
/* 悬停时保持黑色 */
text-decoration: none;
/* 移除下划线 */
}
.action-link.disabled {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
}
.action-link.active {
color: #1890ff;
font-weight: 500;
}
/* 操作图标样式 */
.action-icon {
width: 0.94vw;
/* 18px转换为vw */
height: 0.94vw;
/* 18px转换为vw */
margin-right: 0.26vw;
/* 5px转换为vw */
vertical-align: middle;
display: inline-block;
position: relative;
top: -0.05vw;
/* 微调垂直位置 */
}
.message-list {
display: flex;
flex-direction: column;
gap: 1.04vh;
/* 20px转换为vh消息项之间的间距 */
}
.message-list-container {
display: flex;
flex-direction: column;
gap: 1vh;
}
.message-item {
position: relative;
background: white;
border: 1px solid #D8D8D8;
/* 8px转换为vw */
transition: border-color 0.3s ease;
}
.message-item:hover {
border-color: #1890ff;
}
/* 右上角未读标识 */
.unread-indicator-top-right {
position: absolute;
top: 0.52vh;
/* 10px转换为vh */
right: 0.52vw;
/* 10px转换为vw */
width: 0.42vw;
/* 8px转换为vw */
height: 0.42vw;
/* 8px转换为vw */
background: #ff4d4f;
border-radius: 50%;
}
.message-main {
padding: 1.04vh 1.04vw;
/* 20px转换 */
/* margin-left: 1.04vw; */
/* 为未读标识留出空间 */
}
.message-user {
display: flex;
align-items: flex-start;
gap: 0.73vw;
/* 14px转换为vw */
margin-bottom: 0.73vh;
/* 14px转换为vh */
width: 100%;
/* 确保占满宽度 */
position: relative;
}
/* 消息头像样式 */
.image_22 {
width: 49px;
/* 42px转换为vw */
height: 49px;
/* 42px转换为vw */
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
margin-top: 0.1vh;
/* 2px转换为vh微调位置 */
}
.user-info {
flex: 1;
min-width: 0;
overflow: hidden;
/* 确保内容不会溢出 */
}
.user-content {
margin-bottom: 0.31vh;
/* 6px转换为vh */
overflow: hidden;
/* 隐藏溢出内容 */
}
/* 新的消息样式 */
.user-name {
font-family: PingFangSC, PingFang SC, -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 400;
font-size: 14px;
color: #0388D1;
line-height: 20px;
}
.action-type {
font-family: PingFangSC, PingFang SC, -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 400;
font-size: 14px;
color: #0388D1;
line-height: 20px;
margin-right: 4px;
}
.message-content {
font-family: PingFangSC, PingFang SC, -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 400;
font-size: 14px;
color: #000000;
line-height: 20px;
}
.message-text {
width: auto;
/* 自适应宽度 */
height: auto;
/* 自适应高度 */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 0.73vw;
/* 14px转换为vw */
color: #0388D1;
line-height: 1.04vh;
/* 20px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
display: inline;
/* 改为inline让文字在一行显示 */
white-space: nowrap;
/* 不换行 */
overflow: hidden;
/* 隐藏溢出 */
text-overflow: ellipsis;
/* 超出显示省略号 */
max-width: 100%;
/* 最大宽度为容器宽度 */
}
/* 课程信息容器 */
.course-info-container {
width: 100%;
/* 942px转换为vw */
/* height: 5.21vh; */
padding: 0.52vh 0.57vw;
/* 100px转换为vh进一步增加高度 */
background: #F5F8FB;
background-size: 100% 100%;
margin-top: 0.26vh;
/* 5px转换为vh */
position: relative;
display: flex;
align-items: center;
/* 垂直居中 */
justify-content: flex-start;
/* 水平左对齐 */
padding: 1.04vh 0.57vw;
/* 20px 11px转换为vh/vw增加上下内边距 */
}
.course-label {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: #999999;
line-height: 1.04vh;
/* 20px转换为vh */
}
.course-name {
font-family: PingFangSC, PingFang SC, -apple-system, BlinkMacSystemFont, sans-serif;
font-weight: 400;
font-size: 14px;
color: #608297;
line-height: 20px;
}
.message-time {
position: absolute;
right: 20px;
top: 0.57vw;
/* 90px转换为vw */
height: 1.04vh;
/* 20px转换为vh */
font-size: 0.63vw;
/* 12px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: rgba(153, 153, 153, 1);
text-align: left;
white-space: nowrap;
line-height: 1.04vh;
/* 20px转换为vh */
margin-left: auto;
flex-shrink: 0;
align-self: flex-start;
margin-top: 0.05vh;
/* 1px转换为vh微调位置 */
/* 30px转换为vh让时间下移 */
}
.message-actions-row {
display: flex;
align-items: center;
justify-content: flex-start;
margin-top: 1.56vh;
/* 30px转换为vh增加距离上方的距离 */
margin-bottom: 1.09vh;
/* 21px转换为vh */
margin-left: 0.36vw;
/* 7px转换为vw */
position: relative;
width: 100%;
}
.message-action-btn {
display: flex;
align-items: center;
justify-content: center;
/* 确保内容水平居中 */
gap: 0;
/* 移除图标和文字间距,让它们紧贴 */
padding: 0;
border: none;
background: none;
width: auto;
/* 自适应宽度 */
height: 1.04vh;
/* 20px转换为vh */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 0.73vw;
/* 14px转换为vw */
color: #999999;
line-height: 1.04vh;
/* 20px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
/* 防止文字换行 */
}
/* 回复按钮位置 */
.reply-btn {
margin-left: 2.92vw;
/* 56px转换为vw */
}
/* 删除按钮位置 */
.delete-btn {
margin-left: 0.78vw;
/* 15px转换为vw减小与回复按钮的间距 */
}
/* 举报按钮位置 - 在最右边 */
.report-btn {
position: absolute;
right: 7px;
}
/* 消息操作按钮中的图标样式 */
.message-action-btn .action-icon {
width: 0.73vw;
/* 14px转换为vw */
height: 0.73vw;
/* 14px转换为vw */
margin-right: 0.26vw;
/* 5px转换为vw */
vertical-align: middle;
display: inline-block;
position: relative;
top: -0.03vw;
/* 微调垂直位置 */
}
/* 点赞消息样式 */
.like-message-item {
position: relative;
background: white;
border: 1px solid #D8D8D8;
transition: border-color 0.3s ease;
margin-bottom: 20px;
}
.like-message-item:hover {
border-color: #1890ff;
}
.like-message-main {
padding: 1.04vh 1.04vw;
}
.like-message-user {
display: flex;
align-items: flex-start;
gap: 0.73vw;
margin-bottom: 0.73vh;
}
.like-info {
flex: 1;
min-width: 0;
}
.like-content {
display: flex;
align-items: center;
gap: 0.26vw;
margin-bottom: 0.52vh;
}
.like-content-preview {
margin-top: 0.52vh;
padding: 0.52vh 0.73vw;
background-color: #f5f5f5;
border-radius: 4px;
border-left: 3px solid #1890ff;
}
.content-preview {
font-size: 0.73vw;
color: #666;
font-style: italic;
}
/* 加载状态样式 */
.loading-container {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: white;
border: 1.5px solid #D8D8D8;
border-radius: 0;
margin-bottom: 1.04vh;
}
.loading-text {
color: #666;
font-size: 14px;
}
/* 空状态样式 */
.empty-state {
display: flex;
justify-content: center;
align-items: center;
padding: 40px 20px;
background: white;
border: 1.5px solid #D8D8D8;
border-radius: 0;
margin-bottom: 1.04vh;
}
.empty-text {
color: #999;
font-size: 14px;
}
/* 消息详情容器样式 */
.message-detail-container {
background: white;
border: 1.5px solid #D8D8D8;
border-radius: 0;
margin-bottom: 1.04vh;
min-height: 200px;
}
.message-detail-header {
display: flex;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #e6e6e6;
background: #f8f9fa;
gap: 12px;
}
.back-btn {
background: none;
border: none;
color: #1890ff;
font-size: 14px;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s;
}
.back-btn:hover {
background: #e6f7ff;
}
.detail-title {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.message-detail-content {
padding: 20px;
}
.message-detail-info {
display: flex;
align-items: flex-start;
gap: 16px;
margin-bottom: 20px;
}
.message-detail-icon {
flex-shrink: 0;
}
.message-detail-text {
flex: 1;
}
.message-detail-title {
margin: 0 0 8px 0;
font-size: 16px;
font-weight: 600;
color: #333;
line-height: 1.4;
}
.message-detail-time {
font-size: 14px;
color: #666;
}
.message-detail-body {
padding: 16px;
background: #f8f9fa;
border-radius: 2px;
margin-bottom: 20px;
}
.message-detail-subtitle {
font-size: 14px;
color: #666;
line-height: 1.6;
word-break: break-word;
}
.message-detail-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
}
/* 系统消息样式 */
.system-message-item {
position: relative;
background: white;
border: 1.5px solid #D8D8D8;
border-radius: 0;
transition: border-color 0.3s ease;
height: auto;
min-height: 90px;
/* 132px转换为vh */
margin-bottom: 1.04vh;
display: flex;
align-items: center;
/* 20px转换为vh */
}
.system-message-item:hover {
border-color: #1890ff;
}
.system-message-main {
padding: 10px 0;
/* 24px 20px转换 */
margin-left: 15px;
/* 30px转换为vw */
width: 100%;
}
.system-message-user {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 0.73vw;
/* 14px转换为vw */
margin-bottom: 0.73vh;
/* 14px转换为vh */
width: 100%;
position: relative;
/* 为绝对定位的时间提供参考 */
}
.system-icon {
width: 45px;
/* 42px转换为vw */
height: 45px;
/* 42px转换为vw */
background: rgba(3, 136, 209, 1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 0.1vh;
background-image: url('/images/profile/root.png');
background-size: 100% 100%;
}
.system-info {
flex: 1;
min-width: 0;
overflow: hidden;
}
.system-title {
font-size: 16px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: #333;
line-height: 1.5;
/* 使用相对行高,确保文字不错位 */
text-align: left;
font-style: normal;
text-transform: none;
margin-bottom: 0.52vh;
/* 10px转换为vh */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.system-subtitle-row {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
}
.system-subtitle {
font-size: 14px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: #999;
line-height: 1.5;
/* 使用相对行高,确保文字不错位 */
text-align: left;
font-style: normal;
text-transform: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
/* flex: 1; */
/* 20px转换为vw与查看详情保持间距 */
}
.system-detail-link {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: rgba(3, 136, 209, 1);
line-height: 1.5;
/* 使用相对行高,确保文字不错位 */
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
flex-shrink: 0;
}
.system-detail-link:hover {
text-decoration: underline;
color: #1890ff;
}
.system-time {
padding-right: 20px;
/* 90px转换为vw */
font-size: 12px;
/* 12px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: rgba(153, 153, 153, 1);
text-align: left;
white-space: nowrap;
line-height: 1.5;
/* 使用相对行高,确保文字不错位 */
margin-left: auto;
/* 推到右边 */
flex-shrink: 0;
align-self: flex-start;
}
.message-action-btn:hover {
/* 移除背景色,只保持透明 */
background: none;
color: #999999;
/* 保持原色不变 */
}
.message-action-btn.reply-btn:hover {
background: none;
color: #999999;
/* 保持原色不变 */
}
.message-action-btn.delete-btn:hover {
background: none;
color: #999999;
/* 保持原色不变 */
}
.message-action-btn.report-btn:hover {
background: none;
color: #999999;
/* 保持原色不变 */
}
.reply-section {
margin-top: 0.73vh;
}
.reply-input-container {
padding: 0 0 0 3vw;
}
.reply-textarea {
width: 100%;
min-height: 3.91vh;
/* 75px转换为vh */
border: 1.5px solid #D8D8D8;
/* 6px转换为vw */
padding: 0.52vh 0.52vw;
/* 10px转换 */
font-size: 0.73vw;
/* 14px转换为vw */
line-height: 1.5;
resize: vertical;
font-family: inherit;
}
.reply-textarea:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 0.1vw rgba(24, 144, 255, 0.2);
}
.reply-actions {
display: flex;
justify-content: flex-end;
gap: 0.52vw;
/* 10px转换为vw */
margin-top: 0.52vh;
/* 10px转换为vh */
}
.cancel-btn,
.send-btn {
padding: 0.31vh 0.83vw;
/* 6px 16px转换 */
border: 1px solid #d9d9d9;
border-radius: 0.31vw;
/* 6px转换为vw */
font-size: 0.73vw;
/* 14px转换为vw */
cursor: pointer;
transition: all 0.3s ease;
}
.cancel-btn {
background: white;
color: #666;
}
.cancel-btn:hover {
border-color: #40a9ff;
color: #40a9ff;
}
.send-btn {
width: 80px;
height: 28px;
background: #0288D1;
color: white;
border-color: #1890ff;
border-radius: 0;
margin-top: 5px;
}
.send-btn:hover {
background: #40a9ff;
border-color: #40a9ff;
}
.draftbox {
margin-left: auto;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.draftbox img {
width: 16px;
height: 16px;
margin-right: 8px;
}
.draftbox span {
font-size: 16px;
color: #000;
}
.draftbox:hover {
color: #40a9ff;
}
.breadcrumb-wrapper {
padding: 5px 0;
}
.breadcrumb-wrapper .text_15 {
font-size: 13px;
}
.homework {
color: #A2A2A2;
}
/* 消息页面响应式设计 */
@media (max-width: 1200px) {
.message-header {
flex-direction: column;
align-items: flex-start;
gap: 1vh;
}
.message-tabs {
gap: 3vw;
}
.message-tab-item {
font-size: 1.2vw;
}
}
/* 消息页面响应式设计 */
@media (max-width: 1200px) {
.message-header {
flex-direction: column;
align-items: flex-start;
gap: 1vh;
}
.message-tabs {
gap: 3vw;
}
.message-tab-item {
font-size: 1.2vw;
}
.user-avatar {
width: 3vw;
height: 3vw;
}
.user-name {
font-size: 1.1vw;
}
.message-text {
font-size: 1.1vw;
width: auto;
/* 响应式下自适应宽度 */
height: auto;
/* 响应式下自适应高度 */
line-height: 1.5;
/* 响应式下调整行高 */
white-space: nowrap;
/* 保持不换行 */
overflow: hidden;
/* 隐藏溢出 */
text-overflow: ellipsis;
/* 超出显示省略号 */
}
.course-label,
.course-name,
.message-time {
font-size: 1vw;
}
}
@media (max-width: 768px) {
.message-header {
padding: 0 3vw;
}
.message-tabs {
gap: 5vw;
}
.message-tab-item {
font-size: 3vw;
}
.message-count {
font-size: 2.5vw;
padding: 0.5vh 1.5vw;
}
.action-link {
font-size: 2.8vw;
}
.message-main {
padding: 3vh 3vw;
margin-left: 2vw;
}
.user-avatar {
width: 8vw;
height: 8vw;
}
.user-name {
font-size: 3.2vw;
}
.message-text {
font-size: 3.2vw;
width: auto;
/* 手机端自适应宽度 */
height: auto;
/* 手机端自适应高度 */
line-height: 1.5;
/* 手机端调整行高 */
white-space: nowrap;
/* 保持不换行 */
overflow: hidden;
/* 隐藏溢出 */
text-overflow: ellipsis;
/* 超出显示省略号 */
}
.course-label,
.course-name,
.message-time {
font-size: 2.8vw;
}
/* 手机端课程信息容器调整 */
.course-info-container {
width: 90vw;
height: auto;
padding: 1vh 3vw;
}
/* 手机端系统消息调整 */
.system-message-item {
min-height: 15vh;
}
.system-message-main {
padding: 3vh 3vw;
margin-left: 2vw;
}
.system-icon {
width: 8vw;
height: 8vw;
}
.system-title,
.system-subtitle,
.system-detail-link,
.system-time {
font-size: 2.8vw;
}
.action-btn {
font-size: 2.8vw;
padding: 1vh 2vw;
}
.reply-textarea {
font-size: 3vw;
padding: 2vh 2vw;
}
.cancel-btn,
.send-btn {
font-size: 2.8vw;
padding: 1.5vh 3vw;
}
}
/* 我的下载页面样式 */
.download-content {
background: white;
border-radius: 0.42vw;
/* 8px转换为vw */
padding: 0 0 1.04vh 0;
/* 底部添加20px内边距确保内容不被截断 */
margin: 1.04vh 0;
/* 20px转换为vh */
overflow: visible;
/* 确保内容完全可见 */
}
/* 面包屑控制区域样式 */
.breadcrumb-controls {
display: flex;
justify-content: flex-start;
align-items: center;
padding-top: 1.56vh;
/* 30px 40px转换 */
}
/* 面包屑导航样式 */
.breadcrumb-nav {
display: flex;
align-items: center;
}
.breadcrumb-text {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #666;
cursor: pointer;
transition: color 0.3s ease;
}
.breadcrumb-text:hover {
color: #1890ff;
}
.breadcrumb-current {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #333;
font-weight: 500;
}
/* 子目录中的文件项样式调整 */
.download-content.in-subdirectory .file-item .file-icon {
margin-left: 0 !important;
/* 子目录中移除左边距 */
}
.download-content.in-subdirectory .file-item .file-name {
margin-top: 1.56vh !important;
/* 子目录中文件名下移30px */
}
/* 子目录特有样式 */
.subdirectory-item .file-icon {
margin-left: 0 !important;
/* 移除左边距 */
}
.subdirectory-item .file-name {
margin-top: 2.04vh !important;
/* 20px转换为vh下移文件名 */
}
/* 证书网格特有样式 - 一行显示3个 */
.certificate-grid {
grid-template-columns: repeat(3, 1fr) !important;
/* 固定3列 */
gap: 1.5vw !important;
/* 减少间距,给证书更多空间 */
justify-content: center;
width: 100% !important;
/* 确保占满容器宽度 */
}
.certificate-grid .file-item {
width: 100% !important;
/* 占满网格单元格宽度 */
height: auto !important;
/* 高度自适应内容 */
min-height: 15vh !important;
/* 增加最小高度给证书更多空间 */
background: white !important;
/* 证书背景色改为白色 */
padding: 2.6vh 2.6vw !important;
/* 50px转换为vw/vh上下左右50px间距 */
display: flex !important;
/* 使用flex布局 */
flex-direction: column !important;
/* 垂直排列 */
align-items: center !important;
/* 水平居中 */
justify-content: center !important;
/* 垂直居中 */
}
.certificate-grid .file-item .file-icon {
width: 100% !important;
/* 占满容器宽度 */
height: auto !important;
/* 高度自适应 */
max-width: 20vw !important;
/* 增大最大宽度限制 */
margin: 0 auto 2.6vh auto !important;
/* 居中显示底部留50px间距(2.6vh) */
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.certificate-grid .file-item .folder-icon {
width: 100% !important;
/* 图片占满图标容器 */
height: auto !important;
/* 高度自适应保持比例 */
max-width: 100% !important;
/* 最大宽度不超过容器 */
min-height: 8vh !important;
/* 设置最小高度确保图片足够大 */
object-fit: contain !important;
/* 保持图片比例,完整显示 */
}
.certificate-grid .file-item .file-name {
margin-top: 0.5vh !important;
/* 证书名字与图片间距约25px */
text-align: center !important;
/* 文字居中 */
padding: 0 1.3vw !important;
/* 左右内边距约25px */
}
/* 作业网格特有样式 - 一行显示6个参考图片布局 */
.homework-grid {
grid-template-columns: repeat(6, 1fr) !important;
/* 固定6列 */
gap: 1vw !important;
/* 减少间距 */
justify-content: center;
width: 100% !important;
/* 确保占满容器宽度 */
}
.homework-grid .file-item {
width: 100% !important;
/* 占满网格单元格宽度 */
height: auto !important;
/* 高度自适应内容 */
min-height: 201px !important;
/* 设置最小高度 */
background: white !important;
/* 白色背景 */
padding: 1.5vh 1vw !important;
/* 适当的内边距 */
display: flex !important;
/* 使用flex布局 */
flex-direction: column !important;
/* 垂直排列 */
align-items: center !important;
/* 水平居中 */
justify-content: center !important;
/* 垂直居中 */
border: 1.5px solid #D8D8D8 !important;
/* 添加边框 */
}
.homework-grid .file-item .file-icon {
/* 高度自适应 */
width: 64px;
height: 64px;
/* 限制最大宽度 */
margin: 0 auto 1vh auto !important;
/* 居中显示,底部留间距 */
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.homework-grid .file-item .folder-icon {
width: 100% !important;
/* 图片占满图标容器 */
height: auto !important;
/* 高度自适应保持比例 */
max-width: 100% !important;
/* 最大宽度不超过容器 */
min-height: 4vh !important;
/* 设置最小高度 */
object-fit: contain !important;
/* 保持图片比例,完整显示 */
}
.homework-grid .file-item .file-name {
margin-top: 20px !important;
/* 文件名与图片间距 */
text-align: center !important;
/* 文字居中 */
padding: 0 0.5vw !important;
/* 左右内边距 */
font-size: 14px !important;
/* 较小的字体 */
color: #333 !important;
/* 文字颜色 */
}
.download-header {
padding: 0 0 1vh 0;
/* 30px 40px转换 */
border-bottom: 1px solid #f0f0f0;
}
.download-tabs {
display: flex;
gap: 2.08vw;
/* 40px转换为vw */
}
.download-tab-item {
font-size: 0.83vw;
/* 16px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
color: #666;
cursor: pointer;
padding: 0.52vh 0;
/* 10px转换为vh */
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
}
.download-tab-item.active {
color: #1890ff;
font-weight: 500;
/* 移除蓝色下划线 */
}
.download-tab-item:hover {
color: #1890ff;
}
/* 筛选和操作区域 */
.download-controls {
height: 32px;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30px;
margin-bottom: 10px;
/* padding: 1.56vh 2.08vw; */
/* 30px 40px转换 */
/* 移除下方边框线条 */
}
.download-filters {
display: flex;
align-items: center;
gap: 2.08vw;
/* 40px转换为vw */
}
.filter-group,
.search-group {
display: flex;
align-items: center;
gap: 0.52vw;
/* 10px转换为vw */
}
.filter-label,
.search-label {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #333;
white-space: nowrap;
}
.filter-select {
width: 9.21vw;
/* 100px转换为vw */
height: 32px;
/* 80px转换为vh增加一倍高度 */
padding: 0.52vh 0.52vw;
/* 10px转换增加内边距 */
border: 1px solid #d9d9d9;
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #333;
background: white;
}
.search-input-container {
display: flex;
align-items: center;
position: relative;
}
.search-input {
width: 10.42vw;
/* 200px转换为vw */
height: 32px;
/* 80px转换为vh增加一倍高度 */
padding: 0.26vh 2.08vw 0.26vh 0.52vw;
/* 5px 40px 5px 10px转换右侧留空给按钮 */
border: 1px solid #d9d9d9;
border-radius: 0;
/* 移除圆角,变成直角 */
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #333;
background: white;
}
.search-btn {
position: absolute;
right: 20px;
/* 5px转换为vw */
top: 50%;
transform: translateY(-50%);
width: 16px;
/* 30px转换为vw */
height: 16px;
/* 30px转换为vh */
border: none;
background: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.search-icon {
width: 100%;
height: 100%;
object-fit: contain;
}
.download-actions {
display: flex;
align-items: center;
gap: 1.04vw;
/* 20px转换为vw */
}
.file-count {
/* width: 5.83vw; */
/* 112px转换为vw */
/* height: 0.73vh; */
/* 14px转换为vh */
font-family: AppleSystemUIFont, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 10px;
/* 10px转换为vw */
color: #999999;
line-height: 0.73vh;
/* 14px转换为vh */
text-align: left;
font-style: normal;
text-transform: none;
}
.new-folder-btn {
width: 109px;
height: 31px;
background: #0288D1;
/* 10px 20px转换 */
color: white;
border: none;
/* 4px转换为vw */
font-size: 14px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
cursor: pointer;
transition: all 0.3s ease;
}
.new-folder-btn:hover {
background: #40a9ff;
}
/* 文件网格样式 */
.files-grid {
display: grid;
grid-template-columns: repeat(auto-fit, 9.17vw);
/* 自适应列数每列157px */
gap: 20px;
/* 30px转换为vw增加间距 */
/* padding: 2.08vh 2.08vw 3.13vh 2.08vw; 上40px 左右40px 下60px确保底部边框可见 */
padding: 20px 0;
justify-content: start;
overflow: visible;
/* 确保内容不被截断 */
}
.file-item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 157px;
height: 201px;
/* 157px转换为vw */
min-height: 10.47vh;
/* 最小高度201px允许自适应增长 */
padding: 1.04vh 0.52vw;
/* 上下20px 左右10px */
border: 1px solid #D8D8D8 !important;
/* 强制显示边框 */
border-radius: 0;
/* 移除圆角,变成直角 */
background: white;
transition: all 0.3s ease;
cursor: pointer;
box-sizing: border-box;
margin-bottom: 0.26vh;
/* 2px转换为vh确保底部边框可见 */
}
.file-item:hover {
background: #f5f5f5;
border-color: #bfbfbf;
}
.file-menu {
position: absolute;
top: 0.52vh;
/* 10px转换为vh */
right: 0.52vw;
/* 10px转换为vw */
z-index: 10;
}
.file-menu-btn {
margin-top: 10px;
width: 13px;
height: 13px;
/* 24px转换为vh */
border: none;
background: none;
/* 移除背景 */
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.83vw;
/* 16px转换为vw */
color: #666;
transition: all 0.3s ease;
}
.file-menu-btn:hover {
background: none;
/* 移除悬停背景 */
}
.more-icon {
width: 100%;
height: 100%;
object-fit: contain;
}
.file-menu-dropdown {
position: absolute;
top: 100%;
right: 0;
background: white;
border: 1px solid #d9d9d9;
border-radius: 0.21vw;
/* 4px转换为vw */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 20;
min-width: 4.17vw;
/* 80px转换为vw */
}
.menu-item {
display: flex;
align-items: center;
gap: 0.21vw;
/* 4px转换为vw减小图标和文字间距 */
padding: 0.52vh 0.73vw;
/* 10px 14px转换 */
font-size: 14px;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #000;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
/* 确保文字水平展示,不换行 */
}
.menu-icon {
width: 18px;
/* 84px转换为vw增加一倍 */
height: 18px;
/* 24px转换为vh增加一倍 */
object-fit: contain;
flex-shrink: 0;
}
.menu-item:hover {
background: #f5f5f5;
}
.menu-item:first-child {
border-radius: 0.21vw 0.21vw 0 0;
/* 4px转换为vw */
}
.menu-item:last-child {
border-radius: 0 0 0.21vw 0.21vw;
/* 4px转换为vw */
}
.file-icon {
width: 131px;
/* 100px转换为vw */
height: 131px;
/* 100px转换为vw */
margin-left: 0.5vw;
margin-top: 1.5vh;
/* 30px转换为vh增加顶部间距图片下移 */
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.folder-icon {
width: 100%;
height: 100%;
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
.folder-icon-css {
font-size: 2.5vw;
/* 48px转换为vw */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.file-name {
font-size: 0.73vw;
/* 14px转换为vw */
font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif;
color: #333;
text-align: center;
word-break: break-all;
line-height: 1.2;
margin-top: -0.26vh;
/* 5px转换为vh减小与图标的间距文件名上移 */
margin-bottom: 0.52vh;
/* 10px转换为vh底部间距确保不贴边框 */
padding: 0 0.26vw;
/* 左右5px内边距 */
width: 100%;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.download-tabs {
gap: 3vw;
}
.download-tab-item {
font-size: 1vw;
}
.download-controls {
flex-direction: column;
gap: 2vh;
align-items: flex-start;
}
.download-filters {
gap: 3vw;
}
.filter-select {
width: 8vw;
font-size: 1vw;
}
.search-input {
width: 15vw;
font-size: 1vw;
}
.files-grid {
grid-template-columns: repeat(auto-fit, 12vw);
/* 平板端调整文件项宽度 */
gap: 2vw;
}
.file-item {
width: 12vw;
height: 18vh;
/* 增加平板端高度 */
}
.file-icon {
width: 8vw;
height: 8vw;
}
.file-name {
font-size: 1vw;
}
}
@media (max-width: 1200px) {
}
@media (max-width: 576px) {
.download-content {
margin: 2vh 1vw;
border-radius: 1vw;
}
.download-header {
padding: 3vh 4vw;
}
.download-tabs {
gap: 6vw;
}
.download-tab-item {
font-size: 3vw;
}
.download-controls {
padding: 3vh 4vw;
flex-direction: column;
gap: 3vh;
}
.download-filters {
flex-direction: column;
gap: 2vh;
width: 100%;
}
.filter-group,
.search-group {
width: 100%;
justify-content: space-between;
}
.filter-select {
width: 20vw;
font-size: 3.5vw;
height: 6vh;
}
.search-input {
width: 50vw;
font-size: 3.5vw;
height: 6vh;
padding: 2vh 8vw 2vh 3vw;
}
.search-btn {
width: 6vw;
height: 6vh;
}
.download-actions {
width: 100%;
justify-content: space-between;
}
.file-count {
font-size: 3vw;
}
.new-folder-btn {
font-size: 3vw;
padding: 2vh 4vw;
}
.files-grid {
grid-template-columns: repeat(auto-fit, 40vw);
/* 手机端文件项宽度 */
gap: 4vw;
padding: 4vh 4vw;
justify-content: center;
}
.file-item {
width: 40vw;
height: 30vh;
/* 增加手机端高度 */
}
.file-icon {
width: 20vw;
height: 20vw;
}
.file-name {
font-size: 3vw;
}
.file-menu-btn {
width: 6vw;
height: 6vh;
font-size: 4vw;
}
.menu-item {
font-size: 3.5vw;
padding: 2vh 3vw;
}
}
/* 包含@的消息高亮样式 */
.mention-highlight {
color: #E02424 !important;
}
/* 关注页面样式 */
.follows-content {
width: 100%;
}
.follows-list {
margin-top: 20px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.56vh 1.04vw;
}
.follow-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 2.08vh 0;
background: white;
transition: all 0.3s ease;
text-align: left;
}
.follow-avatar {
margin-right: 1.04vw;
flex-shrink: 0;
}
.avatar-image {
width: 75px;
height: 75px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #f0f0f0;
}
.follow-right-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.78vh;
}
.follow-info {
display: flex;
flex-direction: column;
gap: 0.26vh;
}
.follow-name {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 0.26vh;
}
.follow-role {
font-size: 12px;
color: #707070;
margin-bottom: 0.26vh;
}
.follow-description {
font-size: 0.73vw;
color: #999;
line-height: 1.4;
margin-bottom: 0.26vh;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.follow-visits {
font-size: 0.68vw;
color: #1890ff;
margin-bottom: 0.52vh;
}
.follow-actions {
display: flex;
justify-content: flex-start;
}
.follow-btn {
width: 80px;
height: 23px;
border: none;
font-size: 10px;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
border-radius: 0;
}
/* 取消关注状态 */
.follow-btn.not-following {
background: #0288D1;
color: white;
}
.follow-btn.not-following:hover {
background: #0277BD;
}
/* 已关注状态 */
.follow-btn.following {
background: #F5F8FB;
color: #999999;
}
.follow-btn.following:hover {
background: #E8F4FD;
color: #666666;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.follows-list {
grid-template-columns: repeat(2, 1fr);
}
/* 作业网格响应式 */
.homework-grid {
grid-template-columns: repeat(4, 1fr) !important;
gap: 1.5vw !important;
}
}
@media (max-width: 768px) {
.follows-list {
grid-template-columns: 1fr;
padding: 0 3vw;
gap: 3vh 0;
}
.follow-item {
padding: 4vh 3vw;
border-radius: 1vh;
}
.avatar-image {
width: 15vw;
height: 15vw;
}
.follow-name {
font-size: 4vw;
}
.follow-role {
font-size: 3.5vw;
}
.follow-description {
font-size: 3vw;
}
.follow-visits {
font-size: 3vw;
}
.follow-btn {
padding: 2vh 4vw;
font-size: 3.5vw;
border-radius: 1vh;
}
/* 作业网格移动端响应式 */
.homework-grid {
grid-template-columns: repeat(3, 1fr) !important;
gap: 2vw !important;
}
.homework-grid .file-item {
min-height: 15vh !important;
padding: 2vh 1.5vw !important;
}
.homework-grid .file-item .file-icon {
max-width: 20vw !important;
}
.homework-grid .file-item .file-name {
font-size: 3vw !important;
padding: 0 1vw !important;
}
}
/* 文件列表样式 */
.file-list {
margin-top: 12px;
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.file-list-section .file-item {
display: flex;
align-items: center;
background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 8px 12px;
min-width: 200px;
position: relative;
}
.file-list-section .file-icon {
margin-right: 8px;
}
.file-list-section .file-icon img {
width: 24px;
height: 24px;
}
.file-list-section .file-info {
flex: 1;
min-width: 0;
}
.file-list-section .file-name {
font-size: 12px;
color: #333;
font-weight: 500;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-list-section .file-size {
font-size: 11px;
color: #666;
}
.file-list-section .file-delete {
background: none;
border: none;
cursor: pointer;
padding: 2px;
font-size: 14px;
color: #999;
transition: color 0.2s;
}
.file-delete:hover {
color: #d03050;
}
/* 自定义弹窗样式 */
.custom-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.custom-modal {
background: #F7F7F7;
width: 799px;
max-width: 90vw;
max-height: 758px;
overflow-y: auto;
}
.modal-content {
padding: 22px;
width: 100%;
background: #F7F7F7;
}
/* 弹窗表单标签样式 */
.n-form-item-label {
font-size: 18px !important;
color: #333 !important;
font-weight: 500;
}
/* 弹窗表单项目间距 */
.n-form-item {
margin-bottom: 24px !important;
}
/* 弹窗表单项目内部间距 */
.n-form-item-blank {
margin-top: 8px !important;
}
/* 弹窗底部按钮区域 */
.modal-footer {
display: flex;
gap: 12px;
}
/* 自定义表单样式 */
.form-item {
margin-bottom: 10px;
}
.form-label {
display: block;
font-size: 14px;
color: #333;
font-weight: 500;
margin-bottom: 15px;
}
.required {
color: #d03050;
}
.form-input {
width: 100%;
min-height: 50px;
padding: 12px 16px;
border: 1px solid #e0e0e0;
font-size: 14px;
color: #333;
background: white;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: #18a058;
}
.form-input::placeholder {
color: #999;
}
/* 动态菜单样式 */
[class^="menu-item-"] {
width: 11vw;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
cursor: pointer;
padding: 1.5vh 0 1.5vh 1.8vw;
border-radius: 0.31vw;
transition: all 0.3s ease;
background: transparent;
}
[class^="menu-item-"]:hover {
background: #eff7fc;
}
[class^="menu-item-"].active {
background: #eff7fc;
}
/* 动态菜单图标 */
[class^="menu-item-"] .menu-icon {
width: 1.04vw;
height: 1.04vw;
margin-right: 0.78vw;
object-fit: contain;
flex-shrink: 0;
}
/* 动态菜单文字 */
[class^="menu-item-"] .menu-text {
font-size: 16px;
color: rgba(102, 102, 102, 1);
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-weight: normal;
line-height: 1.46vh;
transition: color 0.3s ease;
}
/* 激活状态的菜单文字颜色 */
[class^="menu-item-"].active .menu-text {
color: rgba(2, 134, 206, 1);
}
/* 悬停状态的菜单文字颜色 */
[class^="menu-item-"]:hover .menu-text {
color: rgba(2, 134, 206, 1);
}
/* 动态菜单图标悬停效果 */
[class^="menu-item-"] .hover-icon {
display: none;
}
[class^="menu-item-"]:hover .default-icon {
display: none;
}
[class^="menu-item-"]:hover .hover-icon {
display: block;
}
/* 激活状态的图标显示 */
[class^="menu-item-"].active .default-icon {
display: none;
}
[class^="menu-item-"].active .hover-icon {
display: block;
}
/* 移动端适配 */
@media (max-width: 768px) {
[class^="menu-item-"] {
width: 80%;
margin: 1.5vh 0;
padding: 2vh 0 2vh 3vw;
}
[class^="menu-item-"] .menu-icon {
width: 6vw;
height: 6vw;
margin-right: 4vw;
}
[class^="menu-item-"] .menu-text {
font-size: 4.5vw;
line-height: 3vh;
}
}
</style>