提交数据
This commit is contained in:
@@ -77,7 +77,8 @@
|
||||
{
|
||||
"path": "project",
|
||||
"style": {
|
||||
"navigationBarTitleText": "包干"
|
||||
"navigationBarTitleText": "包干",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
<text class="section-title">选择项目</text>
|
||||
</view>
|
||||
<!-- 项目选择器 -->
|
||||
<picker mode="selector" :range="projectOptions" range-key="name" :value="selectedProjectIndex" @change="handleProjectChange">
|
||||
<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>
|
||||
@@ -31,7 +32,9 @@
|
||||
</view>
|
||||
<!-- 学生列表 -->
|
||||
<view class="student-list">
|
||||
<view v-for="student in projectStudents" :key="student.id" :class="['student-item', { 'selected': selectedStudentIds.includes(student.id) }]" @click="toggleStudent(student.id)">
|
||||
<view v-for="student in projectStudents" :key="student.id"
|
||||
:class="['student-item', { 'selected': selectedStudentIds.includes(student.id) }]"
|
||||
@click="toggleStudent(student.id)">
|
||||
<!-- 学生头像(使用首字母作为头像) -->
|
||||
<view class="student-avatar">
|
||||
<text class="avatar-text">{{ student.name.charAt(0) }}</text>
|
||||
@@ -75,9 +78,11 @@
|
||||
<text class="chart-desc">历史训练成绩变化曲线</text>
|
||||
</view>
|
||||
<!-- 折线图容器 -->
|
||||
<view class="chart-box">
|
||||
<qiun-data-charts type="line" :opts="lineOpts" :chartData="lineChartData" :ontouch="true" />
|
||||
</view>
|
||||
<scroll-view class="chart-scroll" scroll-x :show-scrollbar="false">
|
||||
<view class="chart-box" :style="{ width: chartWidth + 'rpx' }">
|
||||
<qiun-data-charts type="line" :opts="lineOpts" :chartData="lineChartData" :ontouch="true" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 图例说明 -->
|
||||
@@ -142,8 +147,8 @@
|
||||
* 定义学生的基本信息
|
||||
*/
|
||||
interface Student {
|
||||
id: string
|
||||
name: string
|
||||
id : string
|
||||
name : string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,12 +156,12 @@
|
||||
* 定义学生某次训练的详细记录
|
||||
*/
|
||||
interface StudentTrainingRecord {
|
||||
studentId: string
|
||||
studentName: string
|
||||
time: number
|
||||
recordDate: string
|
||||
recordFullDate: string
|
||||
round: number
|
||||
studentId : string
|
||||
studentName : string
|
||||
time : number
|
||||
recordDate : string
|
||||
recordFullDate : string
|
||||
round : number
|
||||
}
|
||||
|
||||
// ==================== 响应式数据 - 学生相关 ====================
|
||||
@@ -176,6 +181,14 @@
|
||||
|
||||
// ==================== 响应式数据 - 图表相关 ====================
|
||||
|
||||
// 计算图表宽度(根据数据点数量动态调整)
|
||||
const chartWidth = computed(() => {
|
||||
const categoryCount = lineChartData.value.categories.length
|
||||
if (categoryCount <= 6) return 700
|
||||
// 每个数据点分配 100rpx,最小 700rpx
|
||||
return Math.max(700, categoryCount * 100)
|
||||
})
|
||||
|
||||
// 折线图配置选项
|
||||
const lineOpts = ref({
|
||||
color: chartColors,
|
||||
@@ -187,13 +200,14 @@
|
||||
},
|
||||
xAxis: {
|
||||
disableGrid: true,
|
||||
itemCount: 6,
|
||||
rotateLabel: true
|
||||
rotateLabel: true,
|
||||
scrollShow: true,
|
||||
scrollAlign: 'left'
|
||||
},
|
||||
yAxis: {
|
||||
data: [{
|
||||
min: 0,
|
||||
format: (val: number) => val.toFixed(1) + 's'
|
||||
format: (val : number) => val.toFixed(1) + 's'
|
||||
}]
|
||||
},
|
||||
extra: {
|
||||
@@ -226,7 +240,7 @@
|
||||
// ==================== 模拟数据 ====================
|
||||
|
||||
// 模拟的学生数据(按项目分组)
|
||||
const mockProjectStudents: Record<string, Student[]> = {
|
||||
const mockProjectStudents : Record<string, Student[]> = {
|
||||
'1': [
|
||||
{ id: 's1', name: '张小明' },
|
||||
{ id: 's2', name: '李小红' },
|
||||
@@ -252,7 +266,7 @@
|
||||
}
|
||||
|
||||
// 模拟的训练记录数据(按项目分组)
|
||||
const mockTrainingRecords: Record<string, StudentTrainingRecord[]> = {
|
||||
const mockTrainingRecords : Record<string, StudentTrainingRecord[]> = {
|
||||
'1': [
|
||||
{ studentId: 's1', studentName: '张小明', time: 28.5, recordDate: '03-12', recordFullDate: '2026-03-12', round: 1 },
|
||||
{ studentId: 's1', studentName: '张小明', time: 27.2, recordDate: '03-14', recordFullDate: '2026-03-14', round: 1 },
|
||||
@@ -319,7 +333,7 @@
|
||||
// TODO: 调用后端 API 获取项目列表
|
||||
}
|
||||
|
||||
const handleProjectChange = (e: any) => {
|
||||
const handleProjectChange = (e : any) => {
|
||||
const index = e.detail.value
|
||||
selectedProjectIndex.value = index
|
||||
const selectedProject = projectList.value[index]
|
||||
@@ -330,7 +344,7 @@
|
||||
selectedStudentIds.value = []
|
||||
}
|
||||
|
||||
const loadProjectStudents = (projectId: string) => {
|
||||
const loadProjectStudents = (projectId : string) => {
|
||||
if (mockProjectStudents[projectId]) {
|
||||
projectStudents.value = mockProjectStudents[projectId]
|
||||
} else {
|
||||
@@ -338,7 +352,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const toggleStudent = (studentId: string) => {
|
||||
const toggleStudent = (studentId : string) => {
|
||||
const index = selectedStudentIds.value.indexOf(studentId)
|
||||
if (index !== -1) {
|
||||
selectedStudentIds.value.splice(index, 1)
|
||||
@@ -351,7 +365,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const getStudentBestTime = (studentId: string): number => {
|
||||
const getStudentBestTime = (studentId : string) : number => {
|
||||
const records = mockTrainingRecords[selectedProjectId.value] || []
|
||||
const studentRecords = records.filter(r => r.studentId === studentId)
|
||||
if (studentRecords.length === 0) return 0
|
||||
@@ -378,7 +392,7 @@
|
||||
return `${parts[1]}-${parts[2]}`
|
||||
})
|
||||
|
||||
const seriesData: any[] = []
|
||||
const seriesData : any[] = []
|
||||
|
||||
selectedStudentIds.value.forEach((studentId, index) => {
|
||||
const studentRecords = projectRecords.filter(r => r.studentId === studentId)
|
||||
@@ -430,249 +444,255 @@
|
||||
.subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.select-count {
|
||||
font-size: 24rpx;
|
||||
color: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.project-select-section {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 2rpx 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: #52c41a;
|
||||
}
|
||||
|
||||
.picker-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.student-select-section {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 24rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.select-count {
|
||||
font-size: 24rpx;
|
||||
color: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16rpx;
|
||||
.project-select-section {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.student-item {
|
||||
.picker-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 10rpx;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid transparent;
|
||||
border: 1rpx solid #e8e8e8;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #f0f9eb;
|
||||
background-color: #f0f0f0;
|
||||
border-color: #52c41a;
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
|
||||
|
||||
.avatar-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.student-name {
|
||||
font-size: 24rpx;
|
||||
.picker-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
background-color: #52c41a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.chart-section {
|
||||
margin: 0 20rpx;
|
||||
|
||||
.stats-card {
|
||||
.student-select-section {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 2rpx 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: #52c41a;
|
||||
}
|
||||
.section-header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1rpx;
|
||||
height: 50rpx;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 12rpx;
|
||||
border-left: 6rpx solid #52c41a;
|
||||
|
||||
.chart-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.chart-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-box {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
background-color: #fafafa;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-section {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.legend-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.legend-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.student-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16rpx;
|
||||
|
||||
.legend-item {
|
||||
.student-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 20rpx 10rpx;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
|
||||
.legend-color {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #f0f9eb;
|
||||
border-color: #52c41a;
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
|
||||
|
||||
.avatar-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.legend-name {
|
||||
font-size: 26rpx;
|
||||
.student-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.legend-desc {
|
||||
.check-icon {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
background-color: #52c41a;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.chart-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 2rpx 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: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1rpx;
|
||||
height: 50rpx;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 12rpx;
|
||||
border-left: 6rpx solid #52c41a;
|
||||
|
||||
.chart-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.chart-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-scroll {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
background-color: #fafafa;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.chart-box {
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legend-section {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.legend-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.legend-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.legend-color {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.legend-name {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.legend-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,78 +8,81 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择器区域 -->
|
||||
<view class="selector-section">
|
||||
<view class="selector-item" @click="showProjectPicker = true">
|
||||
<text class="selector-label">项目名称</text>
|
||||
<view class="selector-value">
|
||||
<text class="value-text">{{ selectedProject }}</text>
|
||||
<u-icon name="arrow-down" size="24" color="#999"></u-icon>
|
||||
</view>
|
||||
<!-- 项目选择区域 -->
|
||||
<view class="select-section">
|
||||
<view class="select-label">
|
||||
<text class="label-text">选择项目</text>
|
||||
</view>
|
||||
<view class="selector-item" @click="showStudentPicker = true">
|
||||
<text class="selector-label">选择学生</text>
|
||||
<view class="selector-value">
|
||||
<text class="value-text">{{ selectedStudent }}</text>
|
||||
<u-icon name="arrow-down" size="24" color="#999"></u-icon>
|
||||
<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>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 项目选择器 -->
|
||||
<u-picker
|
||||
:show="showProjectPicker"
|
||||
:columns="[projectList]"
|
||||
@confirm="handleProjectConfirm"
|
||||
@cancel="showProjectPicker = false"
|
||||
keyName="label"></u-picker>
|
||||
|
||||
<!-- 学生选择器 -->
|
||||
<u-picker
|
||||
:show="showStudentPicker"
|
||||
:columns="[studentList]"
|
||||
@confirm="handleStudentConfirm"
|
||||
@cancel="showStudentPicker = false"
|
||||
keyName="label"
|
||||
multiple
|
||||
:defaultIndex="defaultStudentIndex"></u-picker>
|
||||
|
||||
<!-- 日历组件 -->
|
||||
<view class="calendar-wrapper">
|
||||
<uni-calendar
|
||||
:insert="true"
|
||||
:lunar="false"
|
||||
:show-month="true"
|
||||
:selected="selectedDates"
|
||||
@monthSwitch="handleMonthSwitch"
|
||||
@change="handleDateChange">
|
||||
<view class="calendar-wrapper" v-if="selectedProjectId">
|
||||
<uni-calendar :insert="true" :range='true' :lunar="false" :show-month="true" :selected="selectedDates"
|
||||
@monthSwitch="handleMonthSwitch" @change="handleDateChange">
|
||||
</uni-calendar>
|
||||
</view>
|
||||
|
||||
<!-- 选中日期数据统计 -->
|
||||
<view class="date-summary" v-if="selectedDate">
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">选中日期</text>
|
||||
<text class="summary-value">{{ selectedDate }}</text>
|
||||
<!-- 学生选择区域 -->
|
||||
<view class="select-section" v-if="selectedProjectId && selectedDate">
|
||||
<view class="select-label">
|
||||
<text class="label-text">选择学生</text>
|
||||
</view>
|
||||
<view class="summary-divider"></view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">训练人数</text>
|
||||
<text class="summary-value">{{ tableData.length }}人</text>
|
||||
</view>
|
||||
<view class="summary-divider"></view>
|
||||
<view class="summary-item">
|
||||
<text class="summary-label">完成率平均</text>
|
||||
<text class="summary-value">{{ averageCompletion }}%</text>
|
||||
<view class="picker-wrapper" @click="showStudentPicker = true">
|
||||
<text class="picker-text">{{ selectedStudentDisplay }}</text>
|
||||
<u-icon name="arrow-down" size="18" color="#999"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 学生多选选择器 -->
|
||||
<view class="modal-overlay" v-if="showStudentPicker" @click="closeStudentPicker"></view>
|
||||
<view class="student-picker-modal" v-if="showStudentPicker">
|
||||
<view class="picker-header">
|
||||
<text class="header-title">选择学生</text>
|
||||
<view class="header-actions">
|
||||
<text class="action-btn" @click="selectAllStudents">全选</text>
|
||||
<text class="action-btn primary" @click="confirmStudentSelection">确定</text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="student-list" scroll-y>
|
||||
<view v-for="(student, index) in studentList" :key="student.id" class="student-item"
|
||||
:class="{ 'selected': selectedStudentIndexes.includes(index) }"
|
||||
@click="toggleStudentSelection(index)">
|
||||
<view class="item-checkbox">
|
||||
<view class="checkbox-inner" :class="{ 'checked': selectedStudentIndexes.includes(index) }">
|
||||
<u-icon v-if="selectedStudentIndexes.includes(index)" name="checkmark" size="14"
|
||||
color="#fff"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-avatar">
|
||||
<view class="avatar-circle"
|
||||
:class="{ 'male': student.gender === '男', 'female': student.gender === '女' }">
|
||||
<text class="avatar-text">{{ student.name.charAt(0) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-info">
|
||||
<text class="item-name">{{ student.name }}</text>
|
||||
<view class="item-meta">
|
||||
<text class="gender-badge"
|
||||
:class="{ 'male': student.gender === '男', 'female': student.gender === '女' }">
|
||||
{{ student.gender }}
|
||||
</text>
|
||||
<text class="age-text">{{ student.age }}岁</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<view class="table-section" v-if="selectedDate">
|
||||
<sl-table
|
||||
:columns="columns"
|
||||
:tableData="tableData"
|
||||
@cell-click="handleCellClick">
|
||||
<view class="table-section" v-if="selectedDate && selectedStudentIndexes.length > 0">
|
||||
<sl-table :columns="columns" :tableData="tableData" @cell-click="handleCellClick">
|
||||
<template #empty>
|
||||
<view class="empty-container">
|
||||
<view class="empty-icon">
|
||||
@@ -100,15 +103,43 @@
|
||||
import { Service } from '@/Service/Service'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
// 当前年月
|
||||
interface Project {
|
||||
id : string
|
||||
name : string
|
||||
}
|
||||
|
||||
interface Student {
|
||||
id : string
|
||||
name : string
|
||||
gender : string
|
||||
age : number
|
||||
}
|
||||
|
||||
interface TableDataItem {
|
||||
date : string
|
||||
name : string
|
||||
projectName : string
|
||||
plan : string
|
||||
completion : number
|
||||
detail : string
|
||||
}
|
||||
|
||||
interface StudentRecord {
|
||||
student : Student
|
||||
date : string
|
||||
segments : TableDataItem[]
|
||||
}
|
||||
|
||||
const currentYear = ref(new Date().getFullYear())
|
||||
const currentMonth = ref(new Date().getMonth() + 1)
|
||||
|
||||
// 选中的日期
|
||||
const selectedDate = ref('')
|
||||
|
||||
// 表格列定义
|
||||
const columns = ref([
|
||||
{
|
||||
label: '日期',
|
||||
prop: 'date',
|
||||
width: '100px'
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
@@ -136,211 +167,218 @@
|
||||
}
|
||||
])
|
||||
|
||||
// 表格数据
|
||||
interface TableDataItem {
|
||||
name: string
|
||||
projectName: string
|
||||
plan: string
|
||||
completion: number
|
||||
detail: string
|
||||
}
|
||||
|
||||
const tableData = ref<TableDataItem[]>([])
|
||||
|
||||
// 项目列表
|
||||
const projectList = ref([
|
||||
{ label: '全部项目', value: 'all' },
|
||||
{ label: '100米自由泳', value: '100m-free' },
|
||||
{ label: '200米自由泳', value: '200m-free' },
|
||||
{ label: '400米自由泳', value: '400m-free' },
|
||||
{ label: '100米蛙泳', value: '100m-breast' }
|
||||
const projectList = ref<Project[]>([
|
||||
{ id: '1', name: '100米自由泳' },
|
||||
{ id: '2', name: '200米自由泳' },
|
||||
{ id: '3', name: '400米自由泳' },
|
||||
{ id: '4', name: '100米蛙泳' }
|
||||
])
|
||||
|
||||
// 学生列表
|
||||
const studentList = ref([
|
||||
{ label: '全部学生', value: 'all' },
|
||||
{ label: '张小明', value: 'zhang-xiaoming' },
|
||||
{ label: '李小红', value: 'li-xiaohong' },
|
||||
{ label: '王小明', value: 'wang-xiaoming' },
|
||||
{ label: '赵小芳', value: 'zhao-xiaofang' },
|
||||
{ label: '陈小刚', value: 'chen-xiaogang' }
|
||||
])
|
||||
const projectOptions = ref(projectList.value)
|
||||
|
||||
// 选中的项目和学生
|
||||
const selectedProject = ref('全部项目')
|
||||
const selectedStudent = ref('全部学生')
|
||||
const selectedProjectValue = ref('all')
|
||||
const selectedStudentValues = ref<string[]>([])
|
||||
const selectedProjectIndex = ref(-1)
|
||||
const selectedProjectId = ref('')
|
||||
|
||||
// 选择器显示状态
|
||||
const showProjectPicker = ref(false)
|
||||
const showStudentPicker = ref(false)
|
||||
const defaultStudentIndex = ref<number[]>([0])
|
||||
|
||||
// 日历打点数据
|
||||
// const selectedDates = ref([
|
||||
// { date: '2026-03-15', info: '训练' },
|
||||
// { date: '2026-03-18', info: '训练' },
|
||||
// { date: '2026-03-20', info: '训练' },
|
||||
// { date: '2026-03-22', info: '训练' },
|
||||
// { date: '2026-03-25', info: '训练' }
|
||||
// ])
|
||||
|
||||
// 平均完成率
|
||||
const averageCompletion = computed(() => {
|
||||
if (tableData.value.length === 0) return 0
|
||||
const total = tableData.value.reduce((sum, item) => sum + item.completion, 0)
|
||||
return Math.round(total / tableData.value.length)
|
||||
const selectedProjectName = computed(() => {
|
||||
if (selectedProjectIndex.value === -1) {
|
||||
return '请选择项目'
|
||||
}
|
||||
return projectList.value[selectedProjectIndex.value]?.name || '请选择项目'
|
||||
})
|
||||
|
||||
// 完整模拟数据
|
||||
const mockData: Record<string, TableDataItem[]> = {
|
||||
'2026-03-28': [
|
||||
{ name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
|
||||
{ name: '张小明', projectName: '200米自由泳', plan: '1500米', completion: 88, detail: '1320/1500' },
|
||||
{ name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 92, detail: '1840/2000' },
|
||||
{ name: '李小红', projectName: '400米自由泳', plan: '1200米', completion: 85, detail: '1020/1200' },
|
||||
{ name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' },
|
||||
{ name: '赵小芳', projectName: '100米蛙泳', plan: '1800米', completion: 88, detail: '1584/1800' },
|
||||
{ name: '陈小刚', projectName: '200米自由泳', plan: '1500米', completion: 95, detail: '1425/1500' }
|
||||
],
|
||||
'2026-03-29': [
|
||||
{ name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' },
|
||||
{ name: '张小明', projectName: '100米蛙泳', plan: '1800米', completion: 85, detail: '1530/1800' },
|
||||
{ name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
|
||||
{ name: '李小红', projectName: '200米自由泳', plan: '1500米', completion: 92, detail: '1380/1500' },
|
||||
{ name: '王小明', projectName: '200米自由泳', plan: '1500米', completion: 98, detail: '1470/1500' },
|
||||
{ name: '赵小芳', projectName: '100米自由泳', plan: '2000米', completion: 82, detail: '1640/2000' },
|
||||
{ name: '陈小刚', projectName: '100米自由泳', plan: '2000米', completion: 88, detail: '1760/2000' }
|
||||
],
|
||||
'2026-03-30': [
|
||||
{ name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 92, detail: '1840/2000' },
|
||||
{ name: '张小明', projectName: '400米自由泳', plan: '1200米', completion: 90, detail: '1080/1200' },
|
||||
{ name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 88, detail: '1760/2000' },
|
||||
{ name: '李小红', projectName: '100米蛙泳', plan: '1800米', completion: 95, detail: '1710/1800' },
|
||||
{ name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
|
||||
{ name: '赵小芳', projectName: '200米自由泳', plan: '1500米', completion: 85, detail: '1275/1500' },
|
||||
{ name: '陈小刚', projectName: '400米自由泳', plan: '1200米', completion: 92, detail: '1104/1200' }
|
||||
]
|
||||
}
|
||||
|
||||
// 日历打点数据
|
||||
const selectedDates = ref([
|
||||
{ date: '2026-03-28', info: '训练' },
|
||||
{ date: '2026-03-29', info: '训练' },
|
||||
{ date: '2026-03-30', info: '训练' }
|
||||
])
|
||||
|
||||
const studentList = ref<Student[]>([])
|
||||
|
||||
const selectedStudentIndexes = ref<number[]>([])
|
||||
const selectedStudentIds = ref<string[]>([])
|
||||
const showStudentPicker = ref(false)
|
||||
|
||||
const selectedStudentDisplay = computed(() => {
|
||||
if (selectedStudentIndexes.value.length === 0) {
|
||||
return '请选择学生'
|
||||
} else {
|
||||
const names = selectedStudentIndexes.value.map(index => studentList.value[index].name)
|
||||
return names.length > 2 ? `${names.slice(0, 2).join(', ')}等${names.length}人` : names.join(', ')
|
||||
}
|
||||
})
|
||||
|
||||
const averageCompletion = computed(() => {
|
||||
if (tableData.value.length === 0) return 0
|
||||
const total = tableData.value.reduce((sum, item) => sum + item.completion, 0)
|
||||
return Math.round(total / tableData.value.length)
|
||||
})
|
||||
|
||||
const mockData : Record<string, Record<string, TableDataItem[]>> = {
|
||||
'1': {
|
||||
'2026-03-28': [
|
||||
{ name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
|
||||
{ name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 92, detail: '1840/2000' },
|
||||
{ name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' }
|
||||
],
|
||||
'2026-03-29': [
|
||||
{ name: '张小明', projectName: '100米自由泳', plan: '2000米', completion: 90, detail: '1800/2000' },
|
||||
{ name: '李小红', projectName: '100米自由泳', plan: '2000米', completion: 95, detail: '1900/2000' },
|
||||
{ name: '王小明', projectName: '100米自由泳', plan: '2000米', completion: 88, detail: '1760/2000' }
|
||||
]
|
||||
},
|
||||
'2': {
|
||||
'2026-03-28': [
|
||||
{ name: '张小明', projectName: '200米自由泳', plan: '1500米', completion: 88, detail: '1320/1500' },
|
||||
{ name: '李小红', projectName: '200米自由泳', plan: '1500米', completion: 92, detail: '1380/1500' },
|
||||
{ name: '王小明', projectName: '200米自由泳', plan: '1500米', completion: 85, detail: '1275/1500' }
|
||||
],
|
||||
'2026-03-29': [
|
||||
{ name: '张小明', projectName: '200米自由泳', plan: '1500米', completion: 90, detail: '1350/1500' },
|
||||
{ name: '李小红', projectName: '200米自由泳', plan: '1500米', completion: 88, detail: '1320/1500' }
|
||||
]
|
||||
},
|
||||
'3': {
|
||||
'2026-03-28': [
|
||||
{ name: '张小明', projectName: '400米自由泳', plan: '1200米', completion: 85, detail: '1020/1200' },
|
||||
{ name: '李小红', projectName: '400米自由泳', plan: '1200米', completion: 90, detail: '1080/1200' }
|
||||
]
|
||||
},
|
||||
'4': {
|
||||
'2026-03-28': [
|
||||
{ name: '赵小芳', projectName: '100米蛙泳', plan: '1800米', completion: 88, detail: '1584/1800' },
|
||||
{ name: '陈小' + '刚', projectName: '100米蛙泳', plan: '1800米', completion: 95, detail: '1710/1800' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const mockStudents : Record<string, Student[]> = {
|
||||
'1': [
|
||||
{ id: 's1', name: '张小明', gender: '男', age: 12 },
|
||||
{ id: 's2', name: '李小红', gender: '女', age: 11 },
|
||||
{ id: 's3', name: '王小明', gender: '男', age: 13 }
|
||||
|
||||
],
|
||||
'2': [
|
||||
{ id: 's1', name: '张小明', gender: '男', age: 12 },
|
||||
{ id: 's2', name: '李小红', gender: '女', age: 11 },
|
||||
{ id: 's3', name: '王小明', gender: '男', age: 13 }
|
||||
|
||||
],
|
||||
'3': [
|
||||
{ id: 's1', name: '张小明', gender: '男', age: 12 },
|
||||
{ id: 's2', name: '李小红', gender: '女', age: 11 }
|
||||
],
|
||||
'4': [
|
||||
{ id: 's4', name: '赵小芳', gender: '女', age: 10 },
|
||||
{ id: 's5', name: '陈小' + '刚', gender: '男', age: 14 }
|
||||
]
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 页面显示时刷新数据
|
||||
|
||||
})
|
||||
|
||||
// 加载数据
|
||||
const loadData = () => {
|
||||
// TODO: 调用API获取数据
|
||||
|
||||
}
|
||||
|
||||
// 月份单元格点击处理
|
||||
const handleCellClick = (event: any) => {
|
||||
const handleCellClick = (event : any) => {
|
||||
console.log('单元格点击事件:', event)
|
||||
}
|
||||
|
||||
// 月份切换处理
|
||||
const handleMonthSwitch = (e: any) => {
|
||||
const handleProjectChange = (e : any) => {
|
||||
const index = e.detail.value
|
||||
selectedProjectIndex.value = index
|
||||
const selectedProject = projectList.value[index]
|
||||
|
||||
if (selectedProject) {
|
||||
selectedProjectId.value = selectedProject.id
|
||||
selectedDate.value = ''
|
||||
selectedStudentIndexes.value = []
|
||||
selectedStudentIds.value = []
|
||||
tableData.value = []
|
||||
loadProjectStudents(selectedProject.id)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMonthSwitch = (e : any) => {
|
||||
currentYear.value = e.year
|
||||
currentMonth.value = e.month
|
||||
loadData()
|
||||
// 切换月份后清空选中日期
|
||||
selectedDate.value = ''
|
||||
selectedStudentIndexes.value = []
|
||||
selectedStudentIds.value = []
|
||||
tableData.value = []
|
||||
}
|
||||
|
||||
// 日期选择处理
|
||||
const handleDateChange = (e: any) => {
|
||||
const handleDateChange = (e : any) => {
|
||||
console.log(e);
|
||||
const date = e.fulldate
|
||||
selectedDate.value = date
|
||||
filterAndLoadData()
|
||||
selectedStudentIndexes.value = []
|
||||
selectedStudentIds.value = []
|
||||
tableData.value = []
|
||||
}
|
||||
|
||||
// 项目选择确认
|
||||
const handleProjectConfirm = (e: any) => {
|
||||
const item = e.value[0]
|
||||
selectedProject.value = item.label
|
||||
selectedProjectValue.value = item.value
|
||||
showProjectPicker.value = false
|
||||
filterAndLoadData()
|
||||
const loadProjectStudents = (projectId : string) => {
|
||||
const students = mockStudents[projectId] || []
|
||||
studentList.value = students
|
||||
console.log(`项目 ${projectId} 的学生列表加载完成,共 ${students.length} 位学生`)
|
||||
}
|
||||
|
||||
// 学生选择确认
|
||||
const handleStudentConfirm = (e: any) => {
|
||||
const items = e.value
|
||||
selectedStudentValues.value = items.map((item: any) => item.value)
|
||||
const selectAllStudents = () => {
|
||||
selectedStudentIndexes.value = studentList.value.map((_, index) => index)
|
||||
}
|
||||
|
||||
if (selectedStudentValues.value.length === 0) {
|
||||
selectedStudent.value = '未选择'
|
||||
} else if (selectedStudentValues.value.includes('all')) {
|
||||
selectedStudent.value = '全部学生'
|
||||
const handleStudentChange = (e : any) => {
|
||||
console.log('学生选择变化:', e)
|
||||
}
|
||||
|
||||
const toggleStudentSelection = (index : number) => {
|
||||
const idx = selectedStudentIndexes.value.indexOf(index)
|
||||
if (idx > -1) {
|
||||
selectedStudentIndexes.value.splice(idx, 1)
|
||||
} else {
|
||||
const selectedNames = items.map((item: any) => item.label)
|
||||
selectedStudent.value = selectedNames.length > 2
|
||||
? `${selectedNames.slice(0, 2).join(', ')}等${selectedNames.length}人`
|
||||
: selectedNames.join(', ')
|
||||
selectedStudentIndexes.value.push(index)
|
||||
}
|
||||
}
|
||||
|
||||
defaultStudentIndex.value = e.indexs
|
||||
const confirmStudentSelection = () => {
|
||||
selectedStudentIds.value = selectedStudentIndexes.value.map(index => studentList.value[index].id)
|
||||
showStudentPicker.value = false
|
||||
filterAndLoadData()
|
||||
}
|
||||
|
||||
// 过滤并加载数据
|
||||
const closeStudentPicker = () => {
|
||||
showStudentPicker.value = false
|
||||
}
|
||||
|
||||
const filterAndLoadData = () => {
|
||||
if (!selectedDate.value) {
|
||||
if (!selectedDate.value || selectedStudentIndexes.value.length === 0) {
|
||||
tableData.value = []
|
||||
return
|
||||
}
|
||||
|
||||
const dateData = mockData[selectedDate.value]
|
||||
const projectData = mockData[selectedProjectId.value]
|
||||
if (!projectData) {
|
||||
tableData.value = []
|
||||
return
|
||||
}
|
||||
|
||||
const dateData = projectData[selectedDate.value]
|
||||
if (!dateData) {
|
||||
tableData.value = []
|
||||
return
|
||||
}
|
||||
|
||||
let filteredData = [...dateData]
|
||||
|
||||
// 按项目过滤
|
||||
if (selectedProjectValue.value !== 'all') {
|
||||
const projectMap: Record<string, string> = {
|
||||
'100m-free': '100米自由泳',
|
||||
'200m-free': '200米自由泳',
|
||||
'400m-free': '400米自由泳',
|
||||
'100m-breast': '100米蛙泳'
|
||||
}
|
||||
const targetProject = projectMap[selectedProjectValue.value]
|
||||
if (targetProject) {
|
||||
filteredData = filteredData.filter(item => item.projectName === targetProject)
|
||||
}
|
||||
}
|
||||
|
||||
// 按学生过滤
|
||||
if (selectedStudentValue.value !== 'all') {
|
||||
const studentMap: Record<string, string> = {
|
||||
'zhang-xiaoming': '张小明',
|
||||
'li-xiaohong': '李小红',
|
||||
'wang-xiaoming': '王小明',
|
||||
'zhao-xiaofang': '赵小芳',
|
||||
'chen-xiaogang': '陈小刚'
|
||||
}
|
||||
const targetStudent = studentMap[selectedStudentValue.value]
|
||||
if (targetStudent) {
|
||||
filteredData = filteredData.filter(item => item.name === targetStudent)
|
||||
}
|
||||
}
|
||||
|
||||
tableData.value = filteredData
|
||||
const selectedStudentNames = selectedStudentIndexes.value.map(index => studentList.value[index].name)
|
||||
tableData.value = dateData.filter(item => selectedStudentNames.includes(item.name))
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -354,7 +392,6 @@
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 页面标题区域 */
|
||||
.header-section {
|
||||
background-color: #fff;
|
||||
padding: 32rpx 28rpx 24rpx;
|
||||
@@ -376,59 +413,272 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 选择器区域 */
|
||||
.selector-section {
|
||||
.select-section {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.selector-item {
|
||||
flex: 1;
|
||||
.select-label {
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.label-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
.selector-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-right: 16rpx;
|
||||
&:active {
|
||||
background-color: #f0f0f0;
|
||||
border-color: #faad14;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.selector-value {
|
||||
flex: 1;
|
||||
.picker-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-wrapper {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.student-picker-modal {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 70vh;
|
||||
background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
z-index: 999;
|
||||
animation: slideUp 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
box-shadow: 0 -8rpx 40rpx rgba(0, 0, 0, 0.12);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
.picker-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);
|
||||
flex-shrink: 0;
|
||||
|
||||
.header-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #faad14 0%, #ffc53d 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
|
||||
.value-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-right: 8rpx;
|
||||
.action-btn {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
padding: 14rpx 28rpx;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(135deg, #f5f5f5 0%, #f0f0f0 100%);
|
||||
font-weight: 600;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
&.primary {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #faad14 0%, #ffc53d 50%, #d48806 100%);
|
||||
box-shadow: 0 4rpx 16rpx rgba(250, 173, 20, 0.3);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0 20rpx 40rpx;
|
||||
|
||||
.student-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
|
||||
border-radius: 20rpx;
|
||||
margin-top: 16rpx;
|
||||
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;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.item-checkbox {
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.checkbox-inner {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 10rpx;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&.checked {
|
||||
border-color: #faad14;
|
||||
background: linear-gradient(135deg, #faad14 0%, #ffc53d 100%);
|
||||
box-shadow: 0 2rpx 8rpx rgba(250, 173, 20, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-avatar {
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.avatar-circle {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.male {
|
||||
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 50%, #096dd9 100%);
|
||||
}
|
||||
|
||||
&.female {
|
||||
background: linear-gradient(135deg, #fa8c16 0%, #ffa940 50%, #d46b08 100%);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-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: 10rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.item-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.gender-badge {
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 10rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&.male {
|
||||
color: #1890ff;
|
||||
background: linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(64, 169, 255, 0.05) 100%);
|
||||
}
|
||||
|
||||
&.female {
|
||||
color: #fa8c16;
|
||||
background: linear-gradient(135deg, rgba(250, 140, 22, 0.1) 0%, rgba(255, 169, 64, 0.05) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.age-text {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 日历组件包装 */
|
||||
.calendar-wrapper {
|
||||
background-color: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 20rpx 0;
|
||||
.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;
|
||||
}
|
||||
|
||||
/* 日期数据统计 */
|
||||
.date-summary {
|
||||
background-color: #fff;
|
||||
margin: 0 20rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -459,16 +709,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.table-section {
|
||||
margin: 0 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 空状态容器 */
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -486,11 +734,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 提示状态 */
|
||||
.hint-state {
|
||||
margin: 0 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 80rpx 40rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
@@ -505,7 +752,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* sl-table 样式覆盖 */
|
||||
::v-deep .sl-table {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -533,4 +779,24 @@
|
||||
::v-deep .sl-table__body__row:nth-child(odd) {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
<!-- 创建项目模块列表 -->
|
||||
<view class="create-modules">
|
||||
<!-- 计时项目模块 -->
|
||||
<view @click="showTimingProjectModal" class="create-card timing-card">
|
||||
<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>
|
||||
@@ -29,7 +29,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 包干项目模块 -->
|
||||
<view @click="createPackageProject" class="create-card package-card">
|
||||
<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>
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
<view class="modal-content">
|
||||
<!-- 新增按钮 -->
|
||||
<view class="add-btn" @click="createTimingProject">
|
||||
<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>
|
||||
@@ -84,7 +84,7 @@
|
||||
<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="Service.GoPage('/pages/userFunc/swiming')">
|
||||
@click=" goPageFunc() ">
|
||||
<view class="item-icon">
|
||||
<view class="icon-bg">
|
||||
<text class="icon-text">{{ project.name.charAt(0) }}</text>
|
||||
@@ -129,6 +129,8 @@
|
||||
}[]
|
||||
}
|
||||
|
||||
let currentIndex=ref(0)
|
||||
|
||||
// 模拟项目数据
|
||||
const projects = ref<Project[]>([
|
||||
{
|
||||
@@ -176,9 +178,20 @@
|
||||
const showDetailModal = ref(false)
|
||||
const currentProject = ref<Project | null>(null)
|
||||
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// TODO: 实际应从接口获取项目列表
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理
|
||||
})
|
||||
|
||||
// 显示计时项目列表弹窗
|
||||
const showTimingProjectModal = () => {
|
||||
const showTimingProjectModal = (index:any) => {
|
||||
showTimingModal.value = true
|
||||
currentIndex.value=index
|
||||
}
|
||||
|
||||
// 关闭计时项目列表弹窗
|
||||
@@ -214,14 +227,16 @@
|
||||
currentProject.value = null
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// TODO: 实际应从接口获取项目列表
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理
|
||||
})
|
||||
const goPageFunc=()=>{
|
||||
if(currentIndex.value==1){
|
||||
Service.GoPage('/pages/userFunc/swiming')
|
||||
}else{
|
||||
Service.GoPage('/pages/userFunc/project')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@
|
||||
{{ student.gender }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">出生日期:</text>
|
||||
<text class="detail-value">{{ student.birthDate }}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">年龄:</text>
|
||||
<text class="detail-value">{{ student.age }}岁</text>
|
||||
@@ -84,18 +88,40 @@
|
||||
<text class="form-label">性别</text>
|
||||
<view class="gender-selector">
|
||||
<view class="gender-option" :class="{ active: formData.gender === '男' }" @click="formData.gender = '男'">
|
||||
<u-icon name="man" size="18" :color="formData.gender === '男' ? '#1890ff' : '#999'"></u-icon>
|
||||
<view class="gender-icon-box">
|
||||
<u-icon name="man" size="20" :color="formData.gender === '男' ? '#fff' : '#1890ff'"></u-icon>
|
||||
</view>
|
||||
<text>男</text>
|
||||
</view>
|
||||
<view class="gender-option" :class="{ active: formData.gender === '女' }" @click="formData.gender = '女'">
|
||||
<u-icon name="woman" size="18" :color="formData.gender === '女' ? '#fa8c16' : '#999'"></u-icon>
|
||||
<view class="gender-icon-box">
|
||||
<u-icon name="woman" size="20" :color="formData.gender === '女' ? '#fff' : '#fa8c16'"></u-icon>
|
||||
</view>
|
||||
<text>女</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">年龄</text>
|
||||
<input class="form-input" v-model="formData.age" placeholder="请输入年龄" type="number" />
|
||||
<text class="form-label">出生日期</text>
|
||||
<view class="date-picker-wrapper">
|
||||
<picker mode="date" :value="formData.birthDate" @change="onBirthDateChange">
|
||||
<view class="form-input date-picker" :class="{ placeholder: !formData.birthDate }">
|
||||
<u-icon name="calendar" size="18" color="#1890ff"></u-icon>
|
||||
<text>{{ formData.birthDate || '请选择出生日期' }}</text>
|
||||
<u-icon name="arrow-down" size="14" color="#999"></u-icon>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view v-if="formData.birthDate" class="age-display">
|
||||
<view class="age-icon">
|
||||
<u-icon name="account" size="24" color="#fff"></u-icon>
|
||||
</view>
|
||||
<view class="age-info">
|
||||
<text class="age-label">年龄</text>
|
||||
<text class="age-value">{{ calculateAge(formData.birthDate) }}</text>
|
||||
<text class="age-unit">岁</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">学校(选填)</text>
|
||||
@@ -128,16 +154,17 @@
|
||||
name : string
|
||||
gender : string
|
||||
age : string
|
||||
birthDate : string
|
||||
school : string
|
||||
address : string
|
||||
}
|
||||
|
||||
// 学员列表
|
||||
const students = ref<Student[]>([
|
||||
{ id: '001', name: '张三', gender: '男', age: '12', school: '第一小学', address: '北京市朝阳区' },
|
||||
{ id: '002', name: '李四', gender: '女', age: '13', school: '', address: '' },
|
||||
{ id: '003', name: '王五', gender: '男', age: '11', school: '第二小学', address: '' },
|
||||
{ id: '004', name: '赵六', gender: '女', age: '12', school: '', address: '上海市浦东新区' }
|
||||
{ id: '001', name: '张三', gender: '男', age: '12', birthDate: '2012-05-15', school: '第一小学', address: '北京市朝阳区' },
|
||||
{ id: '002', name: '李四', gender: '女', age: '13', birthDate: '2011-08-20', school: '', address: '' },
|
||||
{ id: '003', name: '王五', gender: '男', age: '11', birthDate: '2013-03-10', school: '第二小学', address: '' },
|
||||
{ id: '004', name: '赵六', gender: '女', age: '12', birthDate: '2012-11-25', school: '', address: '上海市浦东新区' }
|
||||
])
|
||||
|
||||
// 弹窗状态
|
||||
@@ -150,6 +177,7 @@
|
||||
name: '',
|
||||
gender: '男',
|
||||
age: '',
|
||||
birthDate: '',
|
||||
school: '',
|
||||
address: ''
|
||||
})
|
||||
@@ -165,6 +193,25 @@
|
||||
|
||||
})
|
||||
|
||||
// 计算年龄
|
||||
const calculateAge = (birthDate : string) : number => {
|
||||
if (!birthDate) return 0
|
||||
const birth = new Date(birthDate)
|
||||
const today = new Date()
|
||||
let age = today.getFullYear() - birth.getFullYear()
|
||||
const monthDiff = today.getMonth() - birth.getMonth()
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
||||
age--
|
||||
}
|
||||
return age
|
||||
}
|
||||
|
||||
// 出生日期变化时自动计算年龄
|
||||
const onBirthDateChange = (e : any) => {
|
||||
formData.value.birthDate = e.detail.value
|
||||
formData.value.age = calculateAge(e.detail.value).toString()
|
||||
}
|
||||
|
||||
// 打开编辑弹窗
|
||||
const openEditModal = (student : Student) => {
|
||||
editingStudent.value = student
|
||||
@@ -173,6 +220,7 @@
|
||||
name: student.name,
|
||||
gender: student.gender,
|
||||
age: student.age,
|
||||
birthDate: student.birthDate,
|
||||
school: student.school,
|
||||
address: student.address
|
||||
}
|
||||
@@ -189,6 +237,7 @@
|
||||
name: '',
|
||||
gender: '男',
|
||||
age: '',
|
||||
birthDate: '',
|
||||
school: '',
|
||||
address: ''
|
||||
}
|
||||
@@ -201,8 +250,8 @@
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.value.age.trim()) {
|
||||
Service.Msg('请输入年龄')
|
||||
if (!formData.value.birthDate) {
|
||||
Service.Msg('请选择出生日期')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -212,7 +261,8 @@
|
||||
id: Date.now().toString().slice(-6),
|
||||
name: formData.value.name.trim(),
|
||||
gender: formData.value.gender,
|
||||
age: formData.value.age.trim(),
|
||||
age: formData.value.age,
|
||||
birthDate: formData.value.birthDate,
|
||||
school: formData.value.school.trim(),
|
||||
address: formData.value.address.trim()
|
||||
}
|
||||
@@ -226,7 +276,8 @@
|
||||
...students.value[index],
|
||||
name: formData.value.name.trim(),
|
||||
gender: formData.value.gender,
|
||||
age: formData.value.age.trim(),
|
||||
age: formData.value.age,
|
||||
birthDate: formData.value.birthDate,
|
||||
school: formData.value.school.trim(),
|
||||
address: formData.value.address.trim()
|
||||
}
|
||||
@@ -554,6 +605,101 @@
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 4rpx rgba(24, 144, 255, 0.15);
|
||||
}
|
||||
|
||||
&.date-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12rpx;
|
||||
line-height: 88rpx;
|
||||
|
||||
&.placeholder {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-picker-wrapper {
|
||||
position: relative;
|
||||
|
||||
picker {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.age-display {
|
||||
margin-top: 24rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: 10%;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.age-icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
backdrop-filter: blur(10rpx);
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.age-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8rpx;
|
||||
|
||||
.age-label {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.age-value {
|
||||
font-size: 56rpx;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.age-unit {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gender-selector {
|
||||
@@ -562,17 +708,20 @@
|
||||
|
||||
.gender-option {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
height: 100rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
transition: all 0.25s ease;
|
||||
transition: all 0.3s ease;
|
||||
border: 2rpx solid transparent;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
@@ -580,9 +729,25 @@
|
||||
|
||||
&.active {
|
||||
font-weight: 600;
|
||||
background-color: #e6f7ff;
|
||||
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
||||
border-color: #1890ff;
|
||||
color: #1890ff;
|
||||
color: #fff;
|
||||
box-shadow: 0 6rpx 16rpx rgba(24, 144, 255, 0.3);
|
||||
|
||||
.gender-icon-box {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.gender-icon-box {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
background-color: #e6f7ff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
<text class="time-label">当前总用时</text>
|
||||
<view class="" style="position: relative;">
|
||||
<view class="total-time">{{ formatTime(currentTime) }}</view>
|
||||
<view class="" style="position: absolute; right: 0; bottom: 4rpx;">
|
||||
<view v-if="stopwatchMode!=='together'" @click="recordNextAthlete"
|
||||
style=" display: flex; align-items: center; justify-content: center; background-color: #fff; border: 1rpx solid #e2e2e2; width: 70rpx; height: 70rpx; padding: 10rpx; border-radius: 50%;">
|
||||
<img :src="Service.GetIconImg('/static/index/Flag.png')" style="width: 35rpx; height: 35rpx;"
|
||||
alt="" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -25,7 +18,7 @@
|
||||
|
||||
<view class="athletes-list">
|
||||
<view v-for="(athlete, index) in athletes" :key="athlete.id" @longpress="deleStu(athlete.id)"
|
||||
class="athlete-item" :class="{ finished: athlete.finished, fastest: athlete.isFastest }">
|
||||
class="athlete-item" :class="{ finished: athlete.finished }">
|
||||
<view class="athlete-number">{{ index+1 }}</view>
|
||||
<view class="athlete-info">
|
||||
<text class="athlete-name">{{ athlete.name }}</text>
|
||||
@@ -33,7 +26,7 @@
|
||||
</view>
|
||||
<view class="time-wrapper">
|
||||
<text class="athlete-time">{{ formatTime(athlete.time) }}</text>
|
||||
<text v-if="athlete.bestTime" class="best-time">最快: {{ formatTime(athlete.bestTime) }}</text>
|
||||
<text class="best-time">最快: {{ athlete.bestTime !== null ? formatTime(athlete.bestTime) : '无' }}</text>
|
||||
</view>
|
||||
<view class="" style="display: flex;align-items: center; gap: 10rpx;">
|
||||
<view @click="handleAthleteFirstButton(athlete, index)">
|
||||
@@ -49,18 +42,27 @@
|
||||
|
||||
<!-- 底部控制按钮 -->
|
||||
<view class="control-buttons">
|
||||
<button class="start-btn" @click="startTimer" :disabled="isRunning">
|
||||
<u-icon name="play-right" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">开始</text>
|
||||
</button>
|
||||
<button class="pause-btn" @click="stopTimer" :disabled="!isRunning">
|
||||
<u-icon name="pause" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">暂停</text>
|
||||
</button>
|
||||
<button class="reset-all-btn" @click="resetAll">
|
||||
<u-icon name="reload" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">重置</text>
|
||||
</button>
|
||||
<view class="button-group">
|
||||
<button v-if="!isRunning" class="start-btn" @click="startTimer">
|
||||
<u-icon name="play-right" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">开始</text>
|
||||
</button>
|
||||
|
||||
<button v-if="isRunning" class="record-btn" @click="recordNextAthlete">
|
||||
<u-icon name="checkmark-circle" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">记录</text>
|
||||
</button>
|
||||
</view>
|
||||
<view class="button-group">
|
||||
<button v-if="!isRunning" class="reset-all-btn" @click="resetAll">
|
||||
<u-icon name="reload" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">重置</text>
|
||||
</button>
|
||||
<button v-if="isRunning" class="reset-all-btn" @click="stopTimer">
|
||||
<u-icon name="pause" size="24" color="#fff"></u-icon>
|
||||
<text class="btn-text">暂停</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更多选项弹出框 -->
|
||||
@@ -76,6 +78,11 @@
|
||||
<text class="modal-item-text">添加学生</text>
|
||||
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||
</view>
|
||||
<view class="modal-item" @click="showGroupModal = true; showMoreModal = false">
|
||||
<u-icon name="grid" size="24" color="#fa8c16"></u-icon>
|
||||
<text class="modal-item-text">修改组别</text>
|
||||
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||
</view>
|
||||
<view class="modal-cancel" @click="closeMoreModal">
|
||||
取消
|
||||
</view>
|
||||
@@ -164,13 +171,39 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 修改组别弹出框 -->
|
||||
<view v-if="showGroupModal" class="modal-overlay" @click="closeGroupModal"></view>
|
||||
<view v-if="showGroupModal" class="group-modal">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">修改组别</text>
|
||||
<view class="modal-close" @click="closeGroupModal">
|
||||
<u-icon name="close" size="20" color="#999"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-content">
|
||||
<view class="form-group">
|
||||
<text class="form-label">组别个数</text>
|
||||
<input class="form-input" v-model="groupCount" placeholder="请输入组别个数" type="number" />
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="form-label">一组多少人</text>
|
||||
<input class="form-input" v-model="groupSize" placeholder="请输入一组多少人" type="number" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<button class="modal-cancel-btn" @click="closeGroupModal">取消</button>
|
||||
<button class="modal-confirm-btn" @click="confirmGroupSettings">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮保存按钮 -->
|
||||
<view class="float-save-btn" style="color: #fff; font-size: 24rpx;" @click="saveData">
|
||||
保存
|
||||
</view>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<u-datetime-picker v-model="newStudentBirthday" :show="showDatePicker" mode="date" @confirm="onDateConfirm" @cancel="showDatePicker = false" />
|
||||
<u-datetime-picker v-model="newStudentBirthday" :show="showDatePicker" mode="date" @confirm="onDateConfirm"
|
||||
@cancel="showDatePicker = false" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -189,15 +222,14 @@
|
||||
time : number
|
||||
bestTime : number | null
|
||||
finished : boolean
|
||||
isFastest : boolean
|
||||
}
|
||||
|
||||
// 选手列表
|
||||
const athletes = ref<Athlete[]>([
|
||||
{ id: '1', number: '01', name: '张三', gender: '男', age: '18', birthday: '2006-01-01', time: 0, bestTime: null, finished: false, isFastest: false },
|
||||
{ id: '2', number: '02', name: '李四', gender: '女', age: '17', birthday: '2007-01-01', time: 0, bestTime: null, finished: false, isFastest: false },
|
||||
{ id: '3', number: '03', name: '王五', gender: '男', age: '19', birthday: '2005-01-01', time: 0, bestTime: null, finished: false, isFastest: false },
|
||||
{ id: '4', number: '04', name: '赵六', gender: '女', age: '18', birthday: '2006-01-01', time: 0, bestTime: null, finished: false, isFastest: false }
|
||||
{ id: '1', number: '01', name: '张三', gender: '男', age: '18', birthday: '2006-01-01', time: 0, bestTime: null, finished: false },
|
||||
{ id: '2', number: '02', name: '李四', gender: '女', age: '17', birthday: '2007-01-01', time: 0, bestTime: null, finished: false },
|
||||
{ id: '3', number: '03', name: '王五', gender: '男', age: '19', birthday: '2005-01-01', time: 0, bestTime: null, finished: false },
|
||||
{ id: '4', number: '04', name: '赵六', gender: '女', age: '18', birthday: '2006-01-01', time: 0, bestTime: null, finished: false }
|
||||
])
|
||||
|
||||
// 计时器状态
|
||||
@@ -218,6 +250,11 @@
|
||||
const showMoreModal = ref(false)
|
||||
const showStopwatchModal = ref(false)
|
||||
const showAddStudentModal = ref(false)
|
||||
const showGroupModal = ref(false)
|
||||
|
||||
// 组别设置
|
||||
const groupCount = ref('')
|
||||
const groupSize = ref('')
|
||||
|
||||
// 新学生信息
|
||||
const newStudentName = ref('')
|
||||
@@ -293,6 +330,17 @@
|
||||
|
||||
// 记录下一个选手
|
||||
const recordNextAthlete = () => {
|
||||
// 查找下一个未完成的选手
|
||||
while (currentAthleteIndex.value < athletes.value.length) {
|
||||
const athlete = athletes.value[currentAthleteIndex.value]
|
||||
if (athlete.finished) {
|
||||
// 已手动计时的学生,跳过
|
||||
currentAthleteIndex.value++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAthleteIndex.value >= athletes.value.length) {
|
||||
Service.Msg('所有选手已完成')
|
||||
return
|
||||
@@ -302,14 +350,6 @@
|
||||
athlete.finished = true
|
||||
athlete.time = currentTime.value
|
||||
|
||||
// 更新最快记录
|
||||
if (athlete.bestTime === null || athlete.time < athlete.bestTime) {
|
||||
athlete.bestTime = athlete.time
|
||||
}
|
||||
|
||||
// 更新最快选手
|
||||
updateFastestAthlete()
|
||||
|
||||
currentAthleteIndex.value++
|
||||
|
||||
// 如果所有选手都完成了,停止计时
|
||||
@@ -319,32 +359,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最快选手
|
||||
const updateFastestAthlete = () => {
|
||||
// 先清除所有最快标记
|
||||
athletes.value.forEach(a => a.isFastest = false)
|
||||
|
||||
// 找出已完成的选手
|
||||
const finished = athletes.value.filter(a => a.finished && a.time > 0)
|
||||
if (finished.length === 0) return
|
||||
|
||||
// 找出时间最短的
|
||||
const fastest = finished.reduce((prev, curr) => {
|
||||
return prev.time < curr.time ? prev : curr
|
||||
})
|
||||
|
||||
fastest.isFastest = true
|
||||
}
|
||||
|
||||
// 重置选手时间
|
||||
const resetAthleteTime = (athlete : Athlete) => {
|
||||
athlete.time = 0
|
||||
athlete.finished = false
|
||||
athlete.isFastest = false
|
||||
|
||||
// 重新排序索引
|
||||
reorderAthletes()
|
||||
updateFastestAthlete()
|
||||
}
|
||||
|
||||
// 处理选手第一个按钮点击
|
||||
@@ -358,14 +379,6 @@
|
||||
athlete.finished = true
|
||||
athlete.time = currentTime.value
|
||||
|
||||
// 更新最快记录
|
||||
if (athlete.bestTime === null || athlete.time < athlete.bestTime) {
|
||||
athlete.bestTime = athlete.time
|
||||
}
|
||||
|
||||
// 更新最快选手
|
||||
updateFastestAthlete()
|
||||
|
||||
// 检查是否所有选手都完成了
|
||||
const allFinished = athletes.value.every(a => a.finished)
|
||||
if (allFinished) {
|
||||
@@ -382,6 +395,24 @@
|
||||
currentAthleteIndex.value = athletes.value.length - unfinished.length
|
||||
}
|
||||
|
||||
// 记录所有未完成选手
|
||||
const recordAll = () => {
|
||||
let recordedCount = 0
|
||||
athletes.value.forEach(athlete => {
|
||||
if (!athlete.finished) {
|
||||
athlete.finished = true
|
||||
athlete.time = currentTime.value
|
||||
recordedCount++
|
||||
}
|
||||
})
|
||||
|
||||
if (recordedCount > 0) {
|
||||
Service.Msg(`已记录${recordedCount}位选手`, 'success')
|
||||
} else {
|
||||
Service.Msg('没有需要记录的选手')
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有
|
||||
const resetAll = () => {
|
||||
stopTimer()
|
||||
@@ -391,7 +422,6 @@
|
||||
athletes.value.forEach(athlete => {
|
||||
athlete.time = 0
|
||||
athlete.finished = false
|
||||
athlete.isFastest = false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -476,6 +506,37 @@
|
||||
newStudentAge.value = ''
|
||||
}
|
||||
|
||||
// 关闭组别弹出框
|
||||
const closeGroupModal = () => {
|
||||
showGroupModal.value = false
|
||||
groupCount.value = ''
|
||||
groupSize.value = ''
|
||||
}
|
||||
|
||||
// 确认组别设置
|
||||
const confirmGroupSettings = () => {
|
||||
if (!groupCount.value.trim()) {
|
||||
Service.Msg('请输入组别个数')
|
||||
return
|
||||
}
|
||||
if (!groupSize.value.trim()) {
|
||||
Service.Msg('请输入一组多少人')
|
||||
return
|
||||
}
|
||||
const count = parseInt(groupCount.value)
|
||||
const size = parseInt(groupSize.value)
|
||||
if (isNaN(count) || count <= 0) {
|
||||
Service.Msg('请输入有效的组别个数')
|
||||
return
|
||||
}
|
||||
if (isNaN(size) || size <= 0) {
|
||||
Service.Msg('请输入有效的一组人数')
|
||||
return
|
||||
}
|
||||
Service.Msg(`已设置${count}组,每组${size}人`, 'success')
|
||||
closeGroupModal()
|
||||
}
|
||||
|
||||
// 添加新学生
|
||||
const addNewStudent = () => {
|
||||
if (!newStudentName.value.trim()) {
|
||||
@@ -497,8 +558,7 @@
|
||||
birthday: newStudentBirthday.value.trim(),
|
||||
time: 0,
|
||||
bestTime: null,
|
||||
finished: false,
|
||||
isFastest: false
|
||||
finished: false
|
||||
}
|
||||
|
||||
athletes.value.push(newAthlete)
|
||||
@@ -514,7 +574,7 @@
|
||||
stopwatchMode: stopwatchMode.value,
|
||||
intervalTime: intervalTime.value
|
||||
}
|
||||
Service.SetStorageCache('swimTimingData', data)
|
||||
console.log(data);
|
||||
Service.Msg('保存成功', 'success')
|
||||
}
|
||||
|
||||
@@ -549,7 +609,7 @@
|
||||
.total-time {
|
||||
font-size: 80rpx;
|
||||
font-weight: bold;
|
||||
color: #1890ff;
|
||||
color: #ff0000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,8 +712,8 @@
|
||||
|
||||
.athlete-time {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
color: #ff0000;
|
||||
font-family: 'DIN Alternate', monospace;
|
||||
}
|
||||
|
||||
@@ -680,60 +740,122 @@
|
||||
/* 底部控制按钮 */
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
gap: 24rpx;
|
||||
justify-content: center;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, #ffffff 100%);
|
||||
padding: 32rpx 40rpx;
|
||||
padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
.button-group {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.start-btn,
|
||||
.pause-btn,
|
||||
.reset-all-btn {
|
||||
.reset-all-btn,
|
||||
.record-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
border-radius: 12rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 20rpx;
|
||||
border: none;
|
||||
font-size: 30rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
max-width: 220rpx;
|
||||
gap: 12rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, transparent 100%);
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2rpx;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.5) 50%, transparent 100%);
|
||||
}
|
||||
|
||||
:deep(.u-icon) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
background-color: #52c41a;
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 6rpx 20rpx rgba(82, 196, 26, 0.4);
|
||||
|
||||
&:disabled {
|
||||
background-color: #ccc;
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.pause-btn {
|
||||
background-color: #fa541c;
|
||||
background: linear-gradient(135deg, #fa541c 0%, #ff7a45 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 6rpx 20rpx rgba(250, 84, 28, 0.4);
|
||||
|
||||
&:disabled {
|
||||
background-color: #ccc;
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 2rpx 8rpx rgba(250, 84, 28, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.reset-all-btn {
|
||||
background-color: #1890ff;
|
||||
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 6rpx 20rpx rgba(24, 144, 255, 0.4);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 2rpx 8rpx rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.record-btn {
|
||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
||||
color: #ffffff;
|
||||
box-shadow: 0 6rpx 20rpx rgba(82, 196, 26, 0.4);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 30rpx;
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,9 +907,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 秒表模式和添加学生弹出框 */
|
||||
/* 秒表模式、添加学生和组别弹出框 */
|
||||
.stopwatch-modal,
|
||||
.add-student-modal {
|
||||
.add-student-modal,
|
||||
.group-modal {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@@ -991,15 +1114,36 @@
|
||||
.float-save-btn {
|
||||
position: fixed;
|
||||
right: 40rpx;
|
||||
bottom: 160rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
background: linear-gradient(135deg, #1890ff, #40a9ff);
|
||||
bottom: 180rpx;
|
||||
width: 110rpx;
|
||||
height: 110rpx;
|
||||
background: linear-gradient(135deg, #1890ff 0%, #40a9ff 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.4);
|
||||
box-shadow: 0 6rpx 20rpx rgba(24, 144, 255, 0.4);
|
||||
z-index: 100;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
letter-spacing: 2rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50%;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, transparent 100%);
|
||||
border-radius: 50% 50% 0 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.96);
|
||||
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.3);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user