Files
swimmingUni/src/pages/index/index.vue
2026-04-06 09:31:25 +08:00

937 lines
19 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>
<view class="home-container">
<!-- 空状态 -->
<view class="empty-state-container">
<!-- 标题区域 -->
<view class="title-section">
<text class="main-title">暂无项目</text>
<text class="sub-title">您还没有创建任何训练项目开始您的第一次游泳训练记录吧</text>
</view>
<!-- 创建项目模块列表 -->
<view class="create-modules">
<!-- 计时项目模块 -->
<view @click="showTimingProjectModal(1)" class="create-card timing-card">
<view class="card-icon">
<view class="icon-circle timing-icon">
<u-icon name="clock" size="40" color="#fff"></u-icon>
</view>
</view>
<view class="card-info">
<text class="card-title">计时项目</text>
<text class="card-desc">记录单次训练的计时数据</text>
</view>
<view class="card-arrow">
<u-icon name="arrow-right" size="20" color="#1890ff"></u-icon>
</view>
</view>
<!-- 包干项目模块 -->
<view @click="showTimingProjectModal(2)" class="create-card package-card">
<view class="card-icon">
<view class="icon-circle package-icon">
<u-icon name="grid" size="40" color="#fff"></u-icon>
</view>
</view>
<view class="card-info">
<text class="card-title">包干项目</text>
<text class="card-desc">记录固定距离的训练成绩</text>
</view>
<view class="card-arrow">
<u-icon name="arrow-right" size="20" color="#1890ff"></u-icon>
</view>
</view>
<!-- 分段项目模块 -->
<view @click="createSegmentProject" class="create-card segment-card">
<view class="card-icon">
<view class="icon-circle segment-icon">
<u-icon name="list" size="40" color="#fff"></u-icon>
</view>
</view>
<view class="card-info">
<text class="card-title">分段项目</text>
<text class="card-desc">记录分段训练的详细数据</text>
</view>
<view class="card-arrow">
<u-icon name="arrow-right" size="20" color="#1890ff"></u-icon>
</view>
</view>
</view>
</view>
<!-- 计时项目列表弹窗 -->
<view v-if="showTimingModal" class="modal-overlay" @click="closeTimingProjectModal"></view>
<view v-if="showTimingModal" class="project-list-modal">
<view class="modal-header">
<text class="modal-title">计时项目</text>
<view class="modal-close" @click="closeTimingProjectModal">
<u-icon name="close" size="20" color="#999"></u-icon>
</view>
</view>
<view class="modal-content">
<!-- 新增按钮 -->
<view v-if="currentIndex!==2" class="add-btn" @click="createTimingProject">
<u-icon name="plus" size="18" color="#1890ff"></u-icon>
<text class="add-btn-text">新增项目</text>
</view>
<!-- 项目列表 -->
<view class="project-list">
<text class="list-title" v-if="projects.length > 0">项目列表</text>
<view v-if="projects.length > 0" class="list-container">
<view v-for="project in projects" :key="project.id" class="project-item"
@click=" goPageFunc() ">
<view class="item-icon">
<view class="icon-bg">
<text class="icon-text">{{ project.name.charAt(0) }}</text>
</view>
</view>
<view class="item-info">
<text class="item-name">{{ project.name }}</text>
<text class="item-count">{{ project.studentCount }}</text>
</view>
</view>
</view>
<view v-else class="empty-project">
<text class="empty-text">暂无项目</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { Service } from '@/Service/Service'
// 项目类型
interface Project {
id : string
name : string
startType : string // together | interval
intervalSeconds : string
laneType : string // single | multi
laneCount : string
lanePersonCount : string
studentCount : number
students : {
id : string
name : string
gender : string
age : string
}[]
}
let currentIndex=ref(0)
// 模拟项目数据
const projects = ref<Project[]>([
{
id: '001',
name: '自由泳500米',
startType: 'together',
intervalSeconds: '',
laneType: 'multi',
laneCount: '4',
lanePersonCount: '2',
studentCount: 8,
students: [
{ id: 's1', name: '张三', gender: '男', age: '12' },
{ id: 's2', name: '李四', gender: '女', age: '13' },
{ id: 's3', name: '王五', gender: '男', age: '11' },
{ id: 's4', name: '赵六', gender: '女', age: '12' },
{ id: 's5', name: '钱七', gender: '男', age: '14' },
{ id: 's6', name: '孙八', gender: '女', age: '10' },
{ id: 's7', name: '周九', gender: '男', age: '13' },
{ id: 's8', name: '吴十', gender: '女', age: '12' }
]
},
{
id: '002',
name: '蛙泳200米',
startType: 'interval',
intervalSeconds: '30',
laneType: 'single',
laneCount: '',
lanePersonCount: '',
studentCount: 5,
students: [
{ id: 's1', name: '陈十一', gender: '男', age: '14' },
{ id: 's2', name: '林十二', gender: '女', age: '12' },
{ id: 's3', name: '黄十三', gender: '男', age: '11' },
{ id: 's4', name: '周十四', gender: '女', age: '13' },
{ id: 's5', name: '吴十五', gender: '男', age: '10' }
]
}
])
// 弹窗状态
const showTimingModal = ref(false)
const showDetailModal = ref(false)
const currentProject = ref<Project | null>(null)
// 生命周期
onMounted(() => {
// TODO: 实际应从接口获取项目列表
})
onUnmounted(() => {
// 清理
})
// 显示计时项目列表弹窗
const showTimingProjectModal = (index:any) => {
showTimingModal.value = true
currentIndex.value=index
}
// 关闭计时项目列表弹窗
const closeTimingProjectModal = () => {
showTimingModal.value = false
}
// 创建计时项目
const createTimingProject = () => {
Service.GoPage('/pages/userFunc/setCourse')
}
// 创建包干项目
const createPackageProject = () => {
Service.GoPage('/pages/userFunc/setCourse')
}
// 创建分段项目
const createSegmentProject = () => {
Service.GoPage('/pages/userFunc/setCourse')
}
// 显示项目详情
const showProjectDetail = (project : Project) => {
currentProject.value = project
showTimingModal.value = false
showDetailModal.value = true
}
// 关闭弹窗
const closeDetailModal = () => {
showDetailModal.value = false
currentProject.value = null
}
const goPageFunc=()=>{
if(currentIndex.value==1){
Service.GoPage('/pages/userFunc/swiming')
}else{
Service.GoPage('/pages/userFunc/project')
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.home-container {
padding: 20rpx;
}
/* 项目列表区域 */
.projects-section {
padding: 20rpx 0;
}
/* 计时器卡片 */
.timer-card {
background-color: #fff;
border-radius: 24rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
.timer-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.timer-title {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.task-count {
font-size: 24rpx;
color: #999;
font-weight: 500;
}
}
.timer-content {
display: flex;
align-items: center;
gap: 30rpx;
&:active {
transform: scale(0.98);
}
}
.timer-circle {
position: relative;
}
.circle-progress {
width: 120rpx;
height: 120rpx;
position: relative;
}
.progress-ring {
width: 100%;
height: 100%;
border-radius: 50%;
background: conic-gradient(#1890ff 0deg, #40a9ff 360deg, #1890ff 100%);
display: flex;
align-items: center;
justify-content: center;
animation: progress 1.5s ease-in-out infinite;
}
.ring-inner {
width: 85%;
height: 85%;
background: radial-gradient(circle, #fff 30%, transparent 70%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.15);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
}
}
}
@keyframes progress {
0% {
background: #40a9ff;
}
100% {
background: #1890ff;
}
}
.timer-info {
flex: 1;
.timer-name {
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
display: block;
}
.timer-time {
font-size: 24rpx;
color: #666;
}
}
/* 空状态 */
.empty-state-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 20rpx;
}
/* 标题区域 */
.title-section {
text-align: center;
margin-bottom: 60rpx;
.main-title {
font-size: 42rpx;
font-weight: 700;
color: #333;
display: block;
margin-bottom: 16rpx;
}
.sub-title {
font-size: 26rpx;
color: #999;
line-height: 1.6;
}
}
/* 创建项目模块列表 */
.create-modules {
width: 100%;
max-width: 600rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
/* 创建项目卡片 */
.create-card {
background-color: #fff;
border-radius: 24rpx;
padding: 32rpx 28rpx;
display: flex;
align-items: center;
gap: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
position: relative;
overflow: hidden;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.card-icon {
flex-shrink: 0;
margin-right: 24rpx;
}
.icon-circle {
width: 96rpx;
height: 96rpx;
border-radius: 22rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.12);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&:active {
transform: scale(0.9);
}
/* 计时图标 - 蓝色渐变 */
&.timing-icon {
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 50%, #096dd9 100%);
}
/* 包干图标 - 橙色渐变 */
&.package-icon {
background: linear-gradient(135deg, #faad14 0%, #ffc53d 50%, #d48806 100%);
}
/* 分段图标 - 绿色渐变 */
&.segment-icon {
background: linear-gradient(135deg, #52c41a 0%, #73d13d 50%, #389e0d 100%);
}
}
&::after {
content: '';
position: absolute;
top: 6rpx;
right: 6rpx;
width: 18rpx;
height: 18rpx;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
}
.card-info {
flex: 1;
min-width: 0;
}
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.card-desc {
font-size: 24rpx;
color: #999;
line-height: 1.4;
}
.card-arrow {
flex-shrink: 0;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(24, 144, 255, 0.05) 100%);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&:active {
transform: scale(1.1);
}
}
/* 弹窗通用通用样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.4) 100%);
backdrop-filter: blur(10rpx);
z-index: 998;
animation: fadeIn 0.3s ease;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 36rpx 32rpx 28rpx;
background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
border-bottom: 1rpx solid rgba(0, 0, 0, 0.06);
.modal-title {
font-size: 36rpx;
font-weight: 700;
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.modal-close {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: linear-gradient(135deg, #f5f5f5 0%, #f0f0f0 100%);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
&:active {
background: linear-gradient(135deg, #e8e8e8 0%, #e0e0e0 100%);
transform: scale(0.92);
}
}
}
/* 计时项目列表弹窗 */
.project-list-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 680rpx;
max-height: 80vh;
background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
border-radius: 32rpx;
z-index: 999;
animation: slideUp 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 24rpx 80rpx rgba(0, 0, 0, 0.25), 0 0 0 1rpx rgba(0, 0, 0, 0.04);
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-content {
flex: 1;
padding: 24rpx 28rpx;
overflow-y: auto;
}
/* 新增按钮 */
.add-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
width: 100%;
height: 96rpx;
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 50%, #36cfc9 100%);
background-size: 200% 200%;
border-radius: 20rpx;
margin-bottom: 36rpx;
box-shadow: 0 8rpx 24rpx rgba(24, 144, 255, 0.35), 0 0 0 1rpx rgba(255, 255, 255, 0.5) inset;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shimmer 2s infinite;
}
&:active {
transform: scale(0.97);
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.25);
}
.add-btn-text {
font-size: 32rpx;
color: #fff;
font-weight: 600;
letter-spacing: 2rpx;
position: relative;
z-index: 1;
}
}
/* 项目列表 */
.project-list {
margin-bottom: 20rpx;
.list-title {
font-size: 30rpx;
font-weight: 700;
color: #333;
display: block;
margin-bottom: 24rpx;
padding-left: 12rpx;
border-left: 4rpx solid #1890ff;
background: linear-gradient(90deg, rgba(24, 144, 255, 0.05) 0%, transparent 100%);
padding: 8rpx 12rpx;
border-radius: 0 8rpx 8rpx 0;
}
.list-container {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.project-item {
display: flex;
align-items: center;
gap: 20rpx;
padding: 28rpx 24rpx;
background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
border-radius: 20rpx;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06), 0 0 0 1rpx rgba(0, 0, 0, 0.04) inset;
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4rpx;
height: 100%;
background: linear-gradient(180deg, #1890ff 0%, #40a9ff 100%);
opacity: 0;
transition: opacity 0.3s ease;
}
.item-icon {
flex-shrink: 0;
.icon-bg {
width: 72rpx;
height: 72rpx;
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 50%, #36cfc9 100%);
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.25);
position: relative;
&::before {
content: '';
position: absolute;
top: 8rpx;
right: 8rpx;
width: 12rpx;
height: 12rpx;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
}
.icon-text {
font-size: 34rpx;
color: #fff;
font-weight: 700;
letter-spacing: 2rpx;
}
}
}
.item-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 8rpx;
.item-name {
font-size: 30rpx;
font-weight: 600;
color: #333;
letter-spacing: 1rpx;
}
.item-count {
font-size: 24rpx;
color: #999;
font-weight: 500;
}
}
.item-arrow {
flex-shrink: 0;
width: 52rpx;
height: 52rpx;
border-radius: 50%;
background: linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(64, 169, 255, 0.05) 100%);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
}
.empty-project {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 20rpx;
gap: 16rpx;
.empty-text {
font-size: 28rpx;
color: #999;
font-weight: 500;
}
&::before {
content: '';
width: 120rpx;
height: 120rpx;
background: linear-gradient(135deg, rgba(24, 144, 255, 0.08) 0%, rgba(64, 169, 255, 0.03) 100%);
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
}
}
/* 项目详情弹窗 */
.project-detail-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 680rpx;
max-height: 80vh;
background-color: #fff;
border-radius: 28rpx;
z-index: 999;
animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
overflow: hidden;
display: flex;
flex-direction: column;
}
.detail-section {
padding: 24rpx 0;
margin-bottom: 24rpx;
.detail-title {
font-size: 28rpx;
font-weight: 600;
color: #666;
margin-bottom: 16rpx;
display: block;
padding-left: 8rpx;
border-left: 3rpx solid #1890ff;
}
.detail-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
.detail-label {
font-size: 26rpx;
color: #666;
min-width: 100rpx;
}
.detail-value {
font-size: 28rpx;
color: #333;
font-weight: 500;
flex: 1;
}
}
}
.detail-count {
font-size: 24rpx;
color: #999;
margin-bottom: 16rpx;
display: block;
}
.student-scroll {
max-height: 400rpx;
overflow-y: auto;
.student-item {
display: flex;
align-items: center;
gap: 16rpx;
padding: 16rpx 20rpx;
background-color: #f5f5f5;
border-radius: 16rpx;
margin-bottom: 12rpx;
.student-index {
width: 48rpx;
height: 48rpx;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: #fff;
border-radius: 50%;
font-size: 22rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.student-avatar {
width: 56rpx;
height: 56rpx;
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.avatar-text {
color: #fff;
font-size: 26rpx;
font-weight: 700;
}
}
.student-info {
flex: 1;
min-width: 0;
gap: 8rpx;
}
.student-name {
font-size: 26rpx;
font-weight: 600;
color: #333;
}
.student-meta {
display: flex;
align-items: center;
gap: 12rpx;
.gender-badge {
font-size: 22rpx;
padding: 4rpx 10rpx;
border-radius: 8rpx;
font-weight: 500;
&.male {
color: #1890ff;
background-color: #e6f7ff;
}
&.female {
color: #fa8c16;
background-color: #fff7e6;
}
}
.age-text {
font-size: 22rpx;
color: #999;
}
}
}
}
/* 动画 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translate(-50%, -45%);
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
</style>