页面修改

This commit is contained in:
Ls
2026-03-28 08:55:48 +08:00
parent deaf39e943
commit b8c05d23ae
48 changed files with 9948 additions and 639 deletions

View File

@@ -0,0 +1,535 @@
<template>
<view class="grades-container">
<!-- 页面标题区域 -->
<view class="header-section">
<view class="header-title">
<text class="title">成绩排名</text>
<text class="subtitle">学生最佳成绩排名</text>
</view>
</view>
<!-- ==================== 项目选择区域 ==================== -->
<view class="project-select-section">
<view class="section-header">
<text class="section-title">选择项目</text>
</view>
<!-- 项目选择器 -->
<picker mode="selector" :range="projectOptions" range-key="name" :value="selectedProjectIndex"
@change="handleProjectChange">
<view class="picker-wrapper">
<text class="picker-text">{{ selectedProjectName }}</text>
<u-icon name="arrow-down" size="18" color="#999"></u-icon>
</view>
</picker>
</view>
<!-- ==================== 空状态提示 ==================== -->
<!-- 未选择项目时的提示 -->
<view class="hint-state" v-if="!selectedProjectId">
<view class="hint-icon">
<u-icon name="list" size="80" color="#eb2f96"></u-icon>
</view>
<text class="hint-text">请先选择项目</text>
</view>
<!-- 无数据时的提示 -->
<view class="hint-state" v-else-if="gradeList.length === 0">
<view class="hint-icon">
<u-icon name="file-text" size="80" color="#eb2f96"></u-icon>
</view>
<text class="hint-text">该项目暂无排名数据</text>
</view>
<!-- ==================== 排名列表区域 ==================== -->
<!-- 仅在选择了项目且有数据时显示 -->
<view class="ranking-section" v-if="selectedProjectId && gradeList.length > 0">
<!-- 排名统计卡片 -->
<view class="stats-card">
<view class="stat-item">
<text class="stat-label">对比项目</text>
<text class="stat-value">{{ selectedProjectName }}</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-label">参与人数</text>
<text class="stat-value">{{ gradeList.length }}</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-label">最高速度</text>
<text class="stat-value">{{ maxSpeed }}m/s</text>
</view>
</view>
<!-- 排名列表 -->
<view class="ranking-list">
<view v-for="(item, index) in gradeList" :key="item.id"
:class="['ranking-item', { 'top-three': index < 3 }]" @click="handleRankingClick(item)">
<!-- 排名徽章 -->
<view class="rank-badge" :class="`rank-${index + 1}`">
<text class="rank-number">{{ index + 1 }}</text>
</view>
<!-- 学生头像 -->
<view class="student-avatar">
<text class="avatar-text">{{ item.name.charAt(0) }}</text>
</view>
<!-- 学生信息 -->
<view class="student-info">
<text class="student-name">{{ item.name }}</text>
<text class="student-speed">最快速度{{ item.bestSpeed }}m/s</text>
</view>
<!-- 成绩详情 -->
<view class="speed-detail">
<text class="detail-text">用时{{ item.bestTime }}s</text>
<text class="detail-text">日期{{ item.recordDate }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app"
import { Service } from '@/Service/Service'
import { ref, computed } from 'vue'
// ==================== 响应式数据 - 项目相关 ====================
// 项目列表数据
const projectList = ref([
{ id: '1', name: '100米自由泳' },
{ id: '2', name: '50米自由泳' },
{ id: '3', name: '200米自由泳' },
{ id: '4', name: '100米蛙泳' }
])
// 项目选择器选项(用于 picker 组件)
const projectOptions = ref(projectList.value)
// 当前选中的项目索引(用于 picker 组件的显示)
const selectedProjectIndex = ref(-1)
// 当前选中的项目 ID
const selectedProjectId = ref('')
// 当前选中的项目名称(用于显示)
const selectedProjectName = computed(() => {
if (selectedProjectIndex.value === -1) {
return '请选择项目'
}
return projectList.value[selectedProjectIndex.value]?.name || '请选择项目'
})
// ==================== TypeScript 接口定义 ====================
/**
* 学生成绩接口
* 定义学生的成绩和排名信息
*/
interface StudentGrade {
id : string // 学生 ID
name : string // 学生姓名
bestSpeed : number // 最快速度m/s
bestTime : number // 最快用时(秒)
recordDate : string // 记录日期
studentId : string // 学生 ID用于数据关联
}
// ==================== 响应式数据 - 排名相关 ====================
// 排名列表数据
const gradeList = ref<StudentGrade[]>([])
// 最高速度(计算属性)
const maxSpeed = computed(() => {
if (gradeList.value.length === 0) return 0
return Math.max(...gradeList.value.map(item => item.bestSpeed)).toFixed(2)
})
// ==================== 模拟数据 ====================
/**
* 模拟的学生成绩数据(按项目分组)
* 键为项目 ID值为该项目的学生成绩列表
*/
const mockStudentGrades : Record<string, StudentGrade[]> = {
'1': [
{ id: 'g1', name: '王小明', bestSpeed: 3.89, bestTime: 25.72, recordDate: '03-24', studentId: 's3' },
{ id: 'g2', name: '张小明', bestSpeed: 3.97, bestTime: 25.18, recordDate: '03-24', studentId: 's1' },
{ id: 'g3', name: '赵小芳', bestSpeed: 3.03, bestTime: 32.96, recordDate: '03-14', studentId: 's4' },
{ id: 'g4', name: '李小红', bestSpeed: 3.38, bestTime: 29.58, recordDate: '03-24', studentId: 's2' },
{ id: 'g5', name: '陈小刚', bestSpeed: 2.82, bestTime: 35.46, recordDate: '03-19', studentId: 's5' }
],
'2': [
{ id: 'g6', name: '杨小龙', bestSpeed: 2.05, bestTime: 24.39, recordDate: '03-19', studentId: 's8' },
{ id: 'g7', name: '刘小华', bestSpeed: 2.11, bestTime: 23.70, recordDate: '03-19', studentId: 's6' },
{ id: 'g8', name: '张小丽', bestSpeed: 1.91, bestTime: 26.18, recordDate: '03-16', studentId: 's7' }
],
'3': [
{ id: 'g9', name: '吴小西', bestSpeed: 1.59, bestTime: 125.79, recordDate: '03-16', studentId: 's10' },
{ id: 'g10', name: '黄小东', bestSpeed: 1.61, bestTime: 124.23, recordDate: '03-14', studentId: 's9' },
{ id: 'g11', name: '周小南', bestSpeed: 0.00, bestTime: 0, recordDate: '-', studentId: 's11' },
{ id: 'g12', name: '徐小北', bestSpeed: 0.00, bestTime: 0, recordDate: '-', studentId: 's12' }
],
'4': [
{ id: 'g13', name: '马小兵', bestSpeed: 1.22, bestTime: 81.97, recordDate: '03-14', studentId: 's13' },
{ id: 'g14', name: '朱小红', bestSpeed: 1.13, bestTime: 88.47, recordDate: '03-12', studentId: 's14' }
]
}
// ==================== 生命周期钩子 ====================
/**
* 页面加载时触发
* 在页面初始化时执行,只执行一次
*/
onLoad(() => {
// 初始化数据
loadProjectData()
})
/**
* 页面显示时触发
* 每次页面从后台切换到前台时执行
*/
onShow(() => {
// TODO: 如果需要在页面显示时刷新数据,可以在这里添加逻辑
})
// ==================== 业务逻辑方法 - 项目相关 ====================
/**
* 加载项目数据
* 从后端 API 获取项目列表
*/
const loadProjectData = () => {
// TODO: 调用后端 API 获取项目列表
// 示例代码:
// Service.Request('/api/projects', 'GET').then(res => {
// projectList.value = res.data
// projectOptions.value = res.data
// })
}
/**
* 处理项目选择变化事件
* 当用户选择项目时触发
* @param e picker 选择事件对象,包含 detail.value 属性表示选中的索引
*/
const handleProjectChange = (e : any) => {
// 获取选中的项目索引
const index = e.detail.value
// 更新选中项目的索引
selectedProjectIndex.value = index
// 获取选中的项目
const selectedProject = projectList.value[index]
if (selectedProject) {
// 更新选中项目的 ID
selectedProjectId.value = selectedProject.id
// 加载该项目的排名数据
loadProjectGrades(selectedProject.id)
}
// TODO: 实际项目中应该调用 API 获取该项目的排名数据
}
/**
* 加载项目排名数据
* 根据项目 ID 获取该项目的学生成绩排名列表
* @param projectId 项目 ID
*/
const loadProjectGrades = (projectId : string) => {
// 从模拟数据中获取排名列表
if (mockStudentGrades[projectId]) {
// 按最快速度从高到低排序
gradeList.value = [...mockStudentGrades[projectId]].sort((a, b) => b.bestSpeed - a.bestSpeed)
} else {
gradeList.value = []
}
}
// ==================== 业务逻辑方法 - 排名相关 ====================
/**
* 处理排名项点击事件
* 点击某排名项查看详情
* @param item 排名项数据
*/
const handleRankingClick = (item : StudentGrade) => {
console.log('点击了排名项:', item)
// TODO: 跳转到学生详情页面
// Service.GoPage('/pages/student/detail', { studentId: item.studentId })
}
</script>
<style lang="scss" scoped>
// 页面背景色设置
page {
background-color: #f5f5f5;
}
// 页面容器
.grades-container {
min-height: 100vh;
padding-bottom: 40rpx;
}
/* ==================== 页面标题区域 ==================== */
.header-section {
background-color: #fff;
padding: 32rpx 28rpx 24rpx;
margin-bottom: 20rpx;
.header-title {
.title {
font-size: 36rpx;
font-weight: 700;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.subtitle {
font-size: 24rpx;
color: #999;
}
}
}
/* ==================== 项目选择区域 ==================== */
.project-select-section {
background-color: #fff;
margin: 0 20rpx 20rpx;
border-radius: 16rpx;
padding: 28rpx;
box-shadow: 0 2rpxrpx 12rpx rgba(0, 0, 0, 0.05);
.picker-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
border: 1rpx solid #e8e8e8;
transition: all 0.3s ease;
&:active {
background-color: #f0f0f0;
border-color: #eb2f96;
}
.picker-text {
font-size: 28rpx;
color: #333;
}
}
}
/* ==================== 提示状态容器 ==================== */
.hint-state {
margin: 0 20rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 80rpx 40rpx;
text-align: center;
box-shadow: 0 2rpxrpx 12rpx rgba(0, 0, 0, 0.05);
.hint-icon {
margin-bottom: 24rpx;
}
.hint-text {
font-size: 28rpx;
color: #999;
}
}
/* ==================== 排名列表区域 ==================== */
.ranking-section {
margin: 0 20rpx;
// 统计卡片
.stats-card {
background-color: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: space-around;
box-shadow: 0 2rpxrpx 12rpx rgba(0, 0, 0, 0.05);
.stat-item {
text-align: center;
.stat-label {
font-size: 24rpx;
color: #999;
display: block;
margin-bottom: 8rpx;
}
.stat-value {
font-size: 32rpx;
font-weight: 700;
color: #eb2f96;
}
}
.stat-divider {
width: 1rpx;
height: 50rpx;
background-color: #eee;
}
}
// 排名列表容器
.ranking-list {
// 排名项
.ranking-item {
background-color: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 16rpx;
display: flex;
align-items: center;
gap: 20rpx;
box-shadow: 0 2rpxrpx 12rpx rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
position: relative;
&:active {
transform: scale(0.98);
box-shadow: 0 4rpx 16rpx rgba(235, 47, 150, 0.15);
}
// 前三名特殊样式
&.top-three {
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: linear-gradient(180deg, #eb2f96 0%, #f759ab 100%);
border-radius: 16rpx 0 0 16rpx;
}
}
// 排名徽章
.rank-badge {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background-color: #f0f0f0;
box-shadow: 0 2rpxrpx 8rpx rgba(0, 0, 0, 0.1);
.rank-number {
font-size: 24rpx;
font-weight: 700;
color: #999;
}
// 前三名特殊颜色
&.rank-1 {
background: linear-gradient(135deg, #ffd700 0%, #ffec3d 100%);
box-shadow: 0 2rpxrpx 8rpx rgba(255, 215, 0, 0.4);
.rank-number {
color: #fff;
font-size: 28rpx;
}
}
&.rank-2 {
background: linear-gradient(135deg, #c0c0c0 0%, #d9d9d9 100%);
box-shadow: 0 2rpxrpx 8rpx rgba(192, 192, 192, 0.4);
.rank-number {
color: #fff;
}
}
&.rank-3 {
background: linear-gradient(135deg, #cd7f32 0%, #e6963d 100%);
box-shadow: 0 2rpxrpx 8rpx rgba(205, 127, 50, 0.4);
.rank-number {
color: #fff;
}
}
}
// 学生头像
.student-avatar {
width: 70rpx;
height: 70rpx;
background: linear-gradient(135deg, #eb2f96 0%, #f759ab 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 2rpxrpx 8rpx rgba(235, 47, 150, 0.3);
.avatar-text {
font-size: 28rpx;
font-weight: 700;
color: #fff;
}
}
// 学生信息
.student-info {
flex: 1;
min-width: 0;
.student-name {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.student-speed {
font-size: 26rpx;
color: #eb2f96;
font-weight: 500;
}
}
// 成绩详情
.speed-detail {
display: flex;
align-items: center;
gap: 16rpx;
flex-shrink: 0;
.detail-text {
font-size: 24rpx;
color: #999;
background-color: #f5f5f5;
padding: 6rpx 12rpx;
border-radius: 6rpx;
}
}
}
}
}
</style>