diff --git a/src/Service/BaseConfig.ts b/src/Service/BaseConfig.ts
index f578a9f..0975c90 100644
--- a/src/Service/BaseConfig.ts
+++ b/src/Service/BaseConfig.ts
@@ -1,8 +1,7 @@
export class BaseConfig {
- // protected static servesUrl: string = "http://192.168.0.142:5298";
- // protected static imgUrl: string = "http://192.168.0.142:5298";
- // protected static mediaUrl: string = "http://192.168.0.142:5298/";
-
+ // protected static servesUrl: string = "http://192.168.0.142:5300";
+ // protected static imgUrl: string = "http://192.168.0.142:5300";
+ // protected static mediaUrl: string = "http://192.168.0.142:5300/";
protected static servesUrl: string = "https://swimming.api.xypays.cn";
protected static imgUrl: string = "https://swimming.api.xypays.cn";
diff --git a/src/pages.json b/src/pages.json
index b684ed2..f273e28 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -76,8 +76,9 @@
{
"path": "project",
"style": {
- "navigationBarTitleText": "包干"
-
+ "navigationBarTitleText": "包干",
+ "pageOrientation": "auto",
+ "navigationStyle": "custom"
}
},
{
@@ -89,7 +90,9 @@
{
"path": "hunyang",
"style": {
- "navigationBarTitleText": "混氧"
+ "navigationBarTitleText": "混氧",
+ "pageOrientation": "auto",
+ "navigationStyle": "custom"
}
},
{
@@ -97,12 +100,18 @@
"style": {
"navigationBarTitleText": "创建项目"
}
+ },
+ {
+ "path": "cs",
+ "style": {
+ "navigationBarTitleText": "测试",
+ "pageOrientation": "auto"
+ }
}
]
}, {
"root": "pages/dataAnalyze",
- "pages": [
- {
+ "pages": [{
"path": "baoganAnalyze",
"style": {
"navigationBarTitleText": "包干数据"
diff --git a/src/pages/dataAnalyze/Curve.vue b/src/pages/dataAnalyze/Curve.vue
index e566df7..df6d523 100644
--- a/src/pages/dataAnalyze/Curve.vue
+++ b/src/pages/dataAnalyze/Curve.vue
@@ -11,7 +11,31 @@
-
+
+
+
+
@@ -46,9 +70,6 @@
-
- {{ student.name.charAt(0) }}
-
{{ student.name }}
@@ -58,7 +79,7 @@
-
+
@@ -87,7 +108,7 @@
-
+
@@ -96,6 +117,7 @@
import { Service } from '@/Service/Service'
import { ref, computed } from 'vue'
import { PlanService } from '@/Service/swimming/PlanService'
+ import { studentService } from '@/Service/swimming/studentService'
const MAX_SELECTED_STUDENTS = 3
const chartColors = ['#52c41a', '#1890ff', '#faad14']
@@ -191,9 +213,10 @@
})
}
- const selectProcetFunc = (e: any) => {
- selectProcet.value = e.value[0].name
- selectId.value = e.value[0].planId
+ const selectProcetFunc = (project: any) => {
+ selectProcet.value = project.name
+ selectId.value = project.planId
+ showProject.value = false
selectedStudentIds.value = []
begin.value = ''
end.value = ''
@@ -218,13 +241,14 @@
}
const loadProjectStudents = (projectId: string) => {
- PlanService.GetPlanInfo(projectId).then(res => {
+ studentService.GetStudentList().then(res => {
if (res.code == 0) {
- projectStudents.value = res.data.plan.users
+ projectStudents.value = res.data
} else {
Service.Msg(res.msg)
}
})
+
}
const toggleStudent = (studentId: string) => {
@@ -262,6 +286,7 @@
res.data.list.map((item: any) => {
lineChartData.value.categories.push(item.dayTime)
item.data.map((content: any, index: any) => {
+
xData.value[index].name = content.studentName
xData.value[index].data.push(content.time)
})
@@ -469,6 +494,8 @@
/* 图表区域 */
.chart-section {
margin-top: 4rpx;
+ position: relative;
+ z-index: 1;
}
.stats-card {
@@ -549,4 +576,117 @@
}
}
}
+
+ /* 项目选择弹窗 */
+ .project-popup-content {
+ width: 680rpx;
+ max-height: 70vh;
+ background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
+ border-radius: 32rpx;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 24rpx 80rpx rgba(0, 0, 0, 0.25), 0 0 0 1rpx rgba(0, 0, 0, 0.04);
+ }
+
+ .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);
+ }
+ }
+ }
+
+ .popup-project-list {
+ max-height: 55vh;
+ padding: 24rpx 28rpx;
+ }
+
+ .project-list-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+ }
+
+ .project-item {
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+ padding: 20rpx 24rpx;
+ background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
+ 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;
+ border-radius: 16rpx;
+
+ &::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;
+ }
+
+ &:active::after {
+ opacity: 1;
+ }
+
+ .item-info {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+
+ .item-name {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #333;
+ letter-spacing: 1rpx;
+ }
+ }
+ }
+
+ .empty-project {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 60rpx 0;
+
+ .empty-text {
+ font-size: 28rpx;
+ color: #999;
+ }
+ }
diff --git a/src/pages/dataAnalyze/baoganAnalyze.vue b/src/pages/dataAnalyze/baoganAnalyze.vue
index 572747a..8898ecd 100644
--- a/src/pages/dataAnalyze/baoganAnalyze.vue
+++ b/src/pages/dataAnalyze/baoganAnalyze.vue
@@ -1,5 +1,5 @@
-
+
@@ -7,15 +7,16 @@
筛选条件
-
+
项目
- {{ selectProcet || '请选择项目' }}
+ {{ selectProcet || '请选择项目' }}
-
+
日期
@@ -25,7 +26,7 @@
-
+
学生
@@ -36,40 +37,106 @@
-
+
+
+
+
-
+
训练记录
-
-
+
+
+ {{ item.dayTime }} · {{ item.planName }}
+
+
+
+
+
-
+
- 暂无训练记录
+ {{ isLoading?'暂无训练记录':'加载中' }}
-
-
-
- 请选择筛选条件查看数据
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+ {{ selectedStudentIndexes.indexOf(index) + 1 }}
+
+
+
+ {{ student.name }}
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ student.name.charAt(0) }}
-
- {{ student.name }}
-
-
-
@@ -126,36 +164,29 @@
import { Service } from '@/Service/Service'
import { ref, computed } from 'vue'
import { PlanService } from '@/Service/swimming/PlanService'
-
- interface Project {
- id: string
- name: string
- }
+ import { studentService } from '@/Service/swimming/studentService'
interface Student {
- id: string
- name: string
- gender: string
- age: number
+ studentId : string
+ name : string
+ gender : string
+ age : number
}
interface TableDataItem {
- date: string
- name: string
- projectName: string
- plan: string
- completion: number
- detail: string
+ date : string
+ name : string
+ projectName : string
+ plan : string
+ completion : number
+ detail : string
}
- interface StudentRecord {
- student: Student
- date: string
- segments: TableDataItem[]
- }
+ // 数据加载
+ let isLoading = ref(false)
// 项目选择
- let showProject = ref(false)
+ let showProjectPopup = ref(false)
let selectProcet = ref('')
let selectId = ref('')
@@ -169,40 +200,30 @@
let pageTotal = ref(10)
const columns = ref([
- { label: '训练日期', name: 'addTime' },
{ label: '姓名', name: 'studentName' },
- { label: '项目名称', name: 'name' },
- { label: '项目类型', name: 'planType' },
+ { label: '训练数据', name: 'subsection' },
{ label: '完成比例', name: 'completion' },
{ label: '详细数据', name: 'detail' }
])
-
- let rowData=ref('')
-
- const tableData = ref([])
+
+
+ let row = ref({})
+ let rowData = ref('')
+
+
+ const tableData = ref([])
const projectOptions = ref>([[]])
const studentList = ref([])
const selectedStudentIndexes = ref([])
const selectedStudentIds = ref([])
const showStudentPicker = ref(false)
-
- // 圈数达标时间弹窗
- const showLapsModal = ref(false)
- interface LapsItem {
- lap: number
- time: string
- }
- const lapsData = ref([])
-
- // 打开圈数达标时间弹窗
- const openLapsModal = (data: LapsItem[]) => {
- lapsData.value = data
- showLapsModal.value = true
- }
-
- // 关闭圈数达标时间弹窗
- const closeLapsModal = () => {
- showLapsModal.value = false
+
+ // 日期折叠状态
+ const collapsedDays = ref([])
+
+ // 切换日期折叠状态
+ const toggleDayCollapse = (index : number) => {
+ collapsedDays.value[index] = !collapsedDays.value[index]
}
const selectedStudentDisplay = computed(() => {
@@ -219,12 +240,18 @@
return `${begin.value} 至 ${end.value}`
})
+
onLoad(() => {
- loadData()
getProjectData()
+ getRecordList()
+ getStudentList()
})
- const loadData = () => {}
+ onShow(() => { })
+
+
+
+
const getProjectData = () => {
PlanService.GetPlanListNoPage('计时项目').then(res => {
@@ -235,22 +262,35 @@
}
})
}
-
- const cellClick = (rows: any) => {
- rowData.value = rows.detail
- console.log(rowData.value);
- showLapsModal.value=true
+
+ const getStudentList = () => {
+ studentService.GetStudentList().then(res => {
+ if (res.code == 0) {
+ studentList.value = res.data
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
}
-
- const selectProcetFunc = (e: any) => {
- selectProcet.value = e.value[0].name
- selectId.value = e.value[0].planId
+
+
+ const cellClick = (rows : any) => {
+
+ row.value = rows
+ rowData.value = rows.detail
+ showRecordPopup.value = true
+ }
+
+ const selectProject = (project : any) => {
+ selectProcet.value = project.name
+ selectId.value = project.planId
+ showProjectPopup.value = false
begin.value = ''
end.value = ''
selectedStudentIndexes.value = []
- studentList.value = []
+ selectedStudentIds.value = []
tableData.value = []
- getProjectDetail()
+ getRecord()
}
const getProjectDetail = () => {
@@ -267,21 +307,30 @@
showCalendar.value = true
}
- const calendarConfirm = (e: any) => {
+ const calendarConfirm = (e : any) => {
begin.value = e[0]
end.value = e[e.length - 1]
showCalendar.value = false
+ getRecord()
}
const calendarClose = () => {
showCalendar.value = false
}
- const selectAllStudents = () => {
- selectedStudentIndexes.value = studentList.value.map((_, index) => index)
+ const allSelected = computed(() => {
+ return studentList.value.length > 0 && studentList.value.every((_, index) => selectedStudentIndexes.value.includes(index))
+ })
+
+ const toggleSelectAll = () => {
+ if (allSelected.value) {
+ selectedStudentIndexes.value = []
+ } else {
+ selectedStudentIndexes.value = studentList.value.map((_, index) => index)
+ }
}
- const toggleStudentSelection = (index: number) => {
+ const toggleStudentSelection = (index : number) => {
const idx = selectedStudentIndexes.value.indexOf(index)
if (idx > -1) {
selectedStudentIndexes.value.splice(idx, 1)
@@ -302,6 +351,7 @@
}
const getRecordList = () => {
+ Service.Msg('数据加载中...')
let studentIdList = selectedStudentIndexes.value.map(index => {
return studentList.value[index].studentId
})
@@ -310,21 +360,24 @@
planId: selectId.value,
studentId: JSON.stringify(studentIdList),
sTime: begin.value,
- eTime: end.value,
- page: page.value
+ eTime: end.value
}
PlanService.GetBaoganLog(data).then(res => {
+ isLoading.value = true
+ Service.LoadClose()
if (res.code == 0) {
pageTotal.value = res.data.pageTotal
tableData.value = res.data.list
+ // 初始化折叠状态,默认全部展开
+ collapsedDays.value = new Array(res.data.list.length).fill(false)
} else {
Service.Msg(res.msg)
}
})
}
- const pageChange = (e: any) => {
+ const pageChange = (e : any) => {
page.value = e
getRecordList()
}
@@ -332,16 +385,44 @@
const closeStudentPicker = () => {
showStudentPicker.value = false
}
+
+ // 记录详情弹窗
+ const showRecordPopup = ref(false)
+
+ interface RecordItem {
+ name : string
+ time : string
+ }
+
+ const recordList = ref([])
+
+ // 打开记录详情弹窗
+ const openRecordPopup = (data : RecordItem[]) => {
+ recordList.value = data
+ showRecordPopup.value = true
+ }
+
+ // 关闭记录详情弹窗
+ const closeRecordPopup = () => {
+ showRecordPopup.value = false
+ }
+ // 简化时间格式化(分:秒.毫秒)
+ const formatSimpleTime = (seconds : number) : string => {
+ const mins = Math.floor(seconds / 60)
+ const secs = Math.floor(seconds % 60)
+ const millis = Math.floor((seconds % 1) * 100)
+ return `${mins.toString().padStart(2, '0')}′${secs.toString().padStart(2, '0')}″${millis.toString().padStart(2, '0')}`
+ }
+
\ No newline at end of file
diff --git a/src/pages/dataAnalyze/grades.vue b/src/pages/dataAnalyze/grades.vue
index 1e2332a..2a8ab80 100644
--- a/src/pages/dataAnalyze/grades.vue
+++ b/src/pages/dataAnalyze/grades.vue
@@ -11,7 +11,7 @@
-
+
@@ -53,6 +53,33 @@
暂无排名数据
+
+
+
+
+
+
@@ -100,8 +127,9 @@
}
const selectProcetFunc = (e: any) => {
- selectProcet.value = e.value[0].name
- selectId.value = e.value[0].planId
+
+ selectProcet.value = e.name
+ selectId.value = e.planId
loadProjectGrades(selectId.value)
}
@@ -109,6 +137,7 @@
PlanService.GetRankData(projectId).then(res => {
if (res.code == 0) {
gradeList.value = res.data.list
+ showProject.value=false
} else {
Service.Msg(res.msg)
}
@@ -339,4 +368,141 @@
color: #999;
}
}
+
+
+ /* 项目选择弹窗 */
+ .project-popup-content {
+ width: 680rpx;
+ max-height: 70vh;
+ background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
+ border-radius: 32rpx;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 24rpx 80rpx rgba(0, 0, 0, 0.25), 0 0 0 1rpx rgba(0, 0, 0, 0.04);
+ }
+
+ .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);
+ }
+ }
+ }
+
+ .popup-project-list {
+ max-height: 55vh;
+ padding: 24rpx 28rpx;
+ }
+
+ .project-list-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+ }
+
+ .project-item {
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+ padding: 20rpx 24rpx;
+ background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
+ 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;
+ border-radius: 16rpx;
+
+ &::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;
+ }
+
+ &:active::after {
+ opacity: 1;
+ }
+
+ .item-icon {
+ flex-shrink: 0;
+
+ .icon-bg {
+ width: 56rpx;
+ height: 56rpx;
+ background: linear-gradient(135deg, #1890ff 0%, #40a9ff 50%, #36cfc9 100%);
+ border-radius: 14rpx;
+ 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: 6rpx;
+ right: 6rpx;
+ width: 10rpx;
+ height: 10rpx;
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 50%;
+ }
+
+ .icon-text {
+ font-size: 28rpx;
+ color: #fff;
+ font-weight: 700;
+ letter-spacing: 2rpx;
+ }
+ }
+ }
+
+ .item-info {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+
+ .item-name {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: #333;
+ letter-spacing: 1rpx;
+ }
+ }
+ }
+
diff --git a/src/pages/dataAnalyze/paragraphAnalyze.vue b/src/pages/dataAnalyze/paragraphAnalyze.vue
index 1df5e35..911e4da 100644
--- a/src/pages/dataAnalyze/paragraphAnalyze.vue
+++ b/src/pages/dataAnalyze/paragraphAnalyze.vue
@@ -1,115 +1,155 @@
-
-
-
+
+
+
-
- 选择项目
+
+ 筛选条件
-
- {{ selectProcet || '请选择项目' }}
-
-
-
-
-
-
-
-
- 日期范围
+
+ 项目
+
+ {{ selectProcet || '请选择项目' }}
+
+
-
-
- 开始日期
- {{ begin || '请选择' }}
+
+
+ 日期
+
+
+ {{ dateDisplay }}
+
+
-
-
- 结束日期
- {{ end || '请选择' }}
+
+
+
+ 学生
+
+
+ {{ selectedStudentDisplay }}
+
+
-
-
-
-
-
- 选择学生
+
+
+
-
- {{ selectedStudentDisplay }}
-
-
-
+
-
+
-
- 分段数据
+
+ 训练记录
-
-
+
+
+ {{ item.dayTime }} · {{ item.planName }}
+
+
+
+
+
-
-
- 该日期暂无分段数据
+
+
+ {{ isLoading?'暂无训练记录':'加载中' }}
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+ {{ selectedStudentIndexes.indexOf(index) + 1 }}
+
+
+
+ {{ student.name }}
-
- {{ student.name.charAt(0) }}
-
- {{ student.name }}
-
-
+
+
@@ -116,7 +122,7 @@
let planCount=ref(0)
let studentCount=ref(0)
let userInfo = ref({})
-
+ let isUse=ref(false)
onLoad(() => {
@@ -124,8 +130,19 @@
onShow(() => {
loadUserInfo()
+ isUseFunc()
})
+ const isUseFunc=()=>{
+ userService.IsCheakUserVerify().then(res=>{
+ if(res.code==0){
+ isUse.value=res.data.isCheak
+ }else{
+ Service.Msg(res.msg)
+ }
+ })
+ }
+
const loadUserInfo = () => {
userService.GetUserInfo().then((content:any) => {
if (content.code == 0) {
@@ -138,26 +155,21 @@
})
}
-
- /**
- * 跳转到训练计划页面
- * 点击"训练计划"菜单项时触发
- * 功能说明:
- * - 跳转到训练计划列表页面
- * - 用户可以查看、添加、编辑训练计划
- */
- const goToTrainingPlans = () => {
- Service.GoPage('/pages/userFunc/plan')
+ const goPage=(path:any)=>{
+ if(!isUse.value){
+ Service.Msg('用户暂无权限,请等待审核!')
+ return
+ }
+ Service.GoPage(path)
}
- // 跳转到设置
- const goToSettings = () => {
- Service.Msg('系统设置功能开发中')
- }
-
- // 跳转到关于
- const goToAbout = () => {
- Service.Msg('关于我们功能开发中')
+ const callContact = () => {
+ uni.makePhoneCall({
+ phoneNumber: '13800000000',
+ fail: () => {
+ Service.Msg('拨打电话失败')
+ }
+ })
}
@@ -169,7 +181,7 @@
.user-container {
padding: 20rpx;
padding-top: 30rpx;
- padding-bottom: 40rpx;
+ padding-bottom: 140rpx;
}
/* 用户信息卡片 */
@@ -551,4 +563,30 @@
margin: 0 32rpx;
}
}
+
+ /* 底部固定联系信息 */
+ .contact-footer {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: #fff;
+ border-top: 1rpx solid #eee;
+ padding: 16rpx 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 6rpx;
+ z-index: 10;
+
+ .contact-label {
+ font-size: 24rpx;
+ color: #999;
+ }
+
+ .contact-phone {
+ font-size: 26rpx;
+ color: #1890ff;
+ }
+ }
\ No newline at end of file
diff --git a/src/pages/userFunc/addHunyang.vue b/src/pages/userFunc/addHunyang.vue
index bee767f..6a2227d 100644
--- a/src/pages/userFunc/addHunyang.vue
+++ b/src/pages/userFunc/addHunyang.vue
@@ -378,10 +378,12 @@
console.log('保存的数据:', data)
PlanService.AddPlan(data).then(res => {
if (res.code == 0) {
- Service.Msg('添加成功!')
+ Service.Msg(planId.value?'修改成功!':'添加成功!')
setTimeout(() => {
- Service.GoPageBack()
+ Service.GoPageDelse('/pages/userFunc/hunyang?id=' + res.data.planId)
}, 1000)
+
+
} else {
Service.Msg(res.msg)
}
diff --git a/src/pages/userFunc/cs.vue b/src/pages/userFunc/cs.vue
new file mode 100644
index 0000000..87c611f
--- /dev/null
+++ b/src/pages/userFunc/cs.vue
@@ -0,0 +1,3872 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已完成
+
+
+ 休息
+ {{ formatRestCountdown(timer.restCountdown || 0) }}
+
+
+ 目标时间:{{ formatTime(timer.targetTime) }}
+ {{ formatTime(timer) }}
+
+
+ {{ timer.records.length }}/{{ timer.totalLapCount || 4 }}圈
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加学生到本组
+
+
+ 选择学生
+
+
+
+
+
+ {{ student.name }}
+
+ 暂无可选学生
+
+
+
+
+ 最快时间+几秒
+
+
+
+ 休息时间(秒)
+
+
+
+ 圈数
+
+
+
+
+
+
+
+
+ 取消
+ 确认添加
+
+
+
+
+
+
+
+
+
+
+
+
+ 出发设置
+
+
+
+
+ 一起出发
+
+
+
+
+
+ 间隔出发
+
+
+ 秒
+
+
+ 确定
+
+
+
+
+
+
+ {{ groupSettingMode === 'add' ? '添加分组' : '组别设置' }}
+
+
+ 组名
+
+
+
+ 学生选择
+
+
+
+
+
+ {{ student.name }}
+
+
+
+
+
+ 最快时间+几秒
+
+
+
+ 休息时间
+
+
+
+ 圈数
+
+
+
+
+
+
+
+
+
+ 取消
+
+ {{ groupSettingMode === 'add' ? '确认添加' : '确认' }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/userFunc/hunyang.vue b/src/pages/userFunc/hunyang.vue
index bf5777f..04945fc 100644
--- a/src/pages/userFunc/hunyang.vue
+++ b/src/pages/userFunc/hunyang.vue
@@ -1,100 +1,94 @@
+
+
-
@@ -152,9 +152,10 @@
// 分组设置
const groupSize = ref('0')
-
-
-
+
+
+ // 创建播放器
+ const video = uni.createInnerAudioContext()
// 计时器状态
const isRunning = ref(false)
@@ -169,11 +170,46 @@
const stopwatchMode = ref<'interval' | 'together'>('interval')
// 间隔时间(秒)
const intervalTime = ref('0')
- let record=ref>([])
- let showRecord=ref(false)
+ let record = ref>([])
+ let showRecord = ref(false)
+
+ // 已提示过出发的组号集合(间隔模式用)
+ const notifiedGroupNumbers = ref>(new Set())
+
+ // 可拖动悬浮球
+ const FLOAT_BALL_KEY = 'swiming_float_ball_position'
+ const ballPosition = ref({ x: 20, y: 300 })
+ const startPosition = ref({ x: 0, y: 0 })
+ const touchStartPoint = ref({ x: 0, y: 0 })
+ const ballSize = ref({ w: 50, h: 50 })
+
+ const ballStyle = computed(() => {
+ return `left:${ballPosition.value.x}px;top:${ballPosition.value.y}px;`
+ })
+
onLoad((data : any) => {
planId.value = data.id
+ const sys = uni.getSystemInfoSync()
+ ballSize.value.w = sys.windowWidth * 50 / 375
+ ballSize.value.h = ballSize.value.w
+ ballPosition.value = {
+ x: 20,
+ y: sys.windowHeight - ballSize.value.h - 200
+ }
+
+ const saved = uni.getStorageSync(FLOAT_BALL_KEY)
+ if (saved) {
+ try {
+ const pos = JSON.parse(saved)
+ if (typeof pos.x === 'number' && typeof pos.y === 'number') {
+ ballPosition.value = {
+ x: Math.max(0, Math.min(pos.x, sys.windowWidth - ballSize.value.w)),
+ y: Math.max(0, Math.min(pos.y, sys.windowHeight - ballSize.value.h))
+ }
+ }
+ } catch (e) { }
+ }
})
onShow(() => {
@@ -195,7 +231,22 @@
}
})
}
-
+
+ // 悬浮球事件
+ const ballClick=( data:any)=>{
+ if(data){
+ recordNextAthlete()
+ }else{
+ startTimer()
+ }
+ }
+
+ // 播报
+ const speak=()=>{
+ video.src='/static/icon/video.mp3'
+ video.play()
+ }
+
// 计算分组
const groupedAthletes = computed(() => {
const size = parseInt(groupSize.value) || 2
@@ -243,7 +294,7 @@
const mins = Math.floor(seconds / 60)
const secs = Math.floor(seconds % 60)
const ms = Math.floor((seconds % 1) * 100)
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms.toString().padStart(2, '0')}`
+ return `${mins.toString().padStart(2, '0')}′${secs.toString().padStart(2, '0')}″${ms.toString().padStart(2, '0')}`
}
// 开始计时
@@ -251,12 +302,26 @@
if (isRunning.value) return
isRunning.value = true
+ notifiedGroupNumbers.value.clear()
startTime = Date.now() - currentTime.value * 1000
timerInterval = setInterval(() => {
const elapsed = (Date.now() - startTime) / 1000
currentTime.value = elapsed
+ // 间隔模式下,检查是否有新组到达出发时间
+ if (stopwatchMode.value === 'interval') {
+ const interval = parseFloat(intervalTime.value) || 10
+ groupedAthletes.value.forEach(group => {
+ const groupOffset = (group.groupNumber - 1) * interval
+ if (elapsed >= groupOffset && !notifiedGroupNumbers.value.has(group.groupNumber)) {
+ notifiedGroupNumbers.value.add(group.groupNumber)
+ Service.Msg(`第${group.groupNumber}组出发了`)
+ speak()
+ }
+ })
+ }
+
// 更新未完成选手的时间(考虑偏移量)
athletes.value.forEach(athlete => {
if (!athlete.finished) {
@@ -264,7 +329,7 @@
athlete.time = Math.max(0, elapsed - offset)
}
})
- }, 16)
+ }, 30)
}
// 停止计时
@@ -375,6 +440,7 @@
stopTimer()
currentTime.value = 0
currentAthleteIndex.value = 0
+ notifiedGroupNumbers.value.clear()
athletes.value.forEach(athlete => {
athlete.time = 0
@@ -388,22 +454,18 @@
const mins = Math.floor(seconds / 60)
const secs = Math.floor(seconds % 60)
const ms = Math.floor((seconds % 1) * 1000)
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}`
+ return `${mins.toString().padStart(2, '0')}′${secs.toString().padStart(2, '0')}″${ms.toString().padStart(3, '0')}`
}
// 提交数据
const submitData = () => {
-
+
const hasData = athletes.value.some(a => a.time > 0)
const allFinished = athletes.value.every(a => a.finished)
if (!hasData) {
Service.Msg('暂无数据可提交')
return
}
- // if(!allFinished){
- // Service.Msg('还用学生未完成!')
- // return
- // }
stopTimer()
let data = [{
planName: "",
@@ -417,35 +479,74 @@
planName: planName.value,
studentId: item.studentId,
studentName: item.name,
- time: formatMinuteTime(item.time)
+ time: item.time
})
})
-
- PlanService.AddPlanLog(planId.value,'计时项目',JSON.stringify(data),'','','').then(res=>{
- if(res.code==0){
- Service.Msg('提交成功!')
- stopTimer()
- currentTime.value = 0
- currentAthleteIndex.value = 0
-
- athletes.value.forEach(athlete => {
- athlete.time = 0
- athlete.finished = false
- athlete.startOffset = undefined
- })
-
- if(res.data.record.length>0){
- record.value=res.data.record
- showRecord.value=true
+ uni.showModal({
+ title: '提示', // 对话框标题
+ content: '请确定提交数据!', // 显示的内容
+ showCancel: true, // 是否显示取消按钮
+ cancelText: '取消', // 取消按钮的文字
+ confirmText: '确定', // 确认按钮的文字
+ success: function (res) {
+ if (res.confirm) {
+ PlanService.AddPlanLog(planId.value, '计时项目', JSON.stringify(data), '', '', '').then(res => {
+ if (res.code == 0) {
+ Service.Msg('提交成功!')
+ stopTimer()
+ currentTime.value = 0
+ currentAthleteIndex.value = 0
+
+ athletes.value.forEach(athlete => {
+ athlete.time = 0
+ athlete.finished = false
+ athlete.startOffset = undefined
+ })
+
+ if (res.data.record.length > 0) {
+ record.value = res.data.record
+ showRecord.value = true
+ }
+ } else {
+ Service.Msg(res.msg)
+ }
+ })
+ } else if (res.cancel) {
+ console.log('用户点击取消');
}
- }else{
- Service.Msg(res.msg)
}
- })
+ });
+
}
+ // 悬浮球拖动事件
+ const touchStart = (e : any) => {
+ const touch = e.touches[0]
+ touchStartPoint.value = { x: touch.clientX, y: touch.clientY }
+ startPosition.value = { ...ballPosition.value }
+ }
+
+ const touchMove = (e : any) => {
+ const touch = e.touches[0]
+ const deltaX = touch.clientX - touchStartPoint.value.x
+ const deltaY = touch.clientY - touchStartPoint.value.y
+ ballPosition.value = {
+ x: startPosition.value.x + deltaX,
+ y: startPosition.value.y + deltaY
+ }
+ }
+
+ const touchEnd = () => {
+ const sys = uni.getSystemInfoSync()
+ ballPosition.value = {
+ x: Math.max(0, Math.min(ballPosition.value.x, sys.windowWidth - ballSize.value.w)),
+ y: Math.max(0, Math.min(ballPosition.value.y, sys.windowHeight - ballSize.value.h))
+ }
+ uni.setStorageSync(FLOAT_BALL_KEY, JSON.stringify(ballPosition.value))
+ }
+
// 页面卸载时清理计时器
onUnmounted(() => {
if (timerInterval) {
@@ -479,6 +580,10 @@
font-size: 80rpx;
font-weight: bold;
color: #ff0000;
+ font-family: 'DIN Alternate', monospace;
+ min-width: 400rpx;
+ display: inline-block;
+ text-align: center;
}
.plan-info {
@@ -534,7 +639,7 @@
display: flex;
justify-content: space-between;
align-items: center;
- padding: 24rpx 28rpx;
+ padding: 10rpx 28rpx;
background: linear-gradient(135deg, #e6f7ff 0%, #f0f9ff 100%);
border-left: 6rpx solid #1890ff;
@@ -712,143 +817,111 @@
/* 底部控制按钮 */
.control-buttons {
display: flex;
- gap: 24rpx;
- justify-content: center;
+ gap: 20rpx;
+ justify-content: space-between;
position: fixed;
bottom: 0;
left: 0;
right: 0;
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));
+ padding: 24rpx 32rpx;
+ padding-bottom: calc(24rpx + 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 {
+ .ctrl-btn {
flex: 1;
- display: flex;
- justify-content: center;
- }
-
- .record-group {
- flex: 4;
- }
-
- .pause-group {
- flex: 1;
- }
-
- .back-btn,
- .start-btn,
- .pause-btn,
- .reset-all-btn,
- .record-btn,
- .submit-btn {
- flex: 1;
- height: 100rpx;
- border-radius: 20rpx;
+ height: 92rpx;
+ border-radius: 24rpx;
border: none;
- font-size: 32rpx;
+ font-size: 30rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
- 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: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
- color: #ffffff;
- box-shadow: 0 6rpx 20rpx rgba(82, 196, 26, 0.4);
+ gap: 10rpx;
+ transition: all 0.25s ease;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12);
&:active {
transform: scale(0.96);
- box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
}
}
- .pause-btn {
- background: linear-gradient(135deg, #fa541c 0%, #ff7a45 100%);
- color: #ffffff;
- box-shadow: 0 6rpx 20rpx rgba(250, 84, 28, 0.4);
-
- &:active {
- transform: scale(0.96);
- box-shadow: 0 2rpx 8rpx rgba(250, 84, 28, 0.3);
- }
+ .btn-submit {
+ background: linear-gradient(135deg, #faad14 0%, #ffc53d 100%);
+ color: #fff;
+ box-shadow: 0 6rpx 18rpx rgba(250, 173, 20, 0.35);
}
- .reset-all-btn {
+ .btn-data {
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);
- }
+ color: #fff;
+ box-shadow: 0 6rpx 18rpx rgba(24, 144, 255, 0.35);
}
- .back-btn {
- background: linear-gradient(135deg, #722ed1 0%, #9254de 100%);
- color: #ffffff;
- box-shadow: 0 6rpx 20rpx rgba(114, 46, 209, 0.4);
-
- &:active {
- transform: scale(0.96);
- box-shadow: 0 2rpx 8rpx rgba(114, 46, 209, 0.3);
- }
- }
-
- .record-btn {
+ .btn-reset {
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
- color: #ffffff;
- box-shadow: 0 6rpx 20rpx rgba(82, 196, 26, 0.4);
+ color: #fff;
+ box-shadow: 0 6rpx 18rpx rgba(82, 196, 26, 0.35);
+ }
- &:active {
- transform: scale(0.96);
- box-shadow: 0 2rpx 8rpx rgba(82, 196, 26, 0.3);
- }
+ .btn-pause {
+ background: linear-gradient(135deg, #fa541c 0%, #ff7a45 100%);
+ color: #fff;
+ box-shadow: 0 6rpx 18rpx rgba(250, 84, 28, 0.35);
}
.btn-text {
- font-size: 32rpx;
+ font-size: 30rpx;
color: #ffffff;
font-weight: 600;
letter-spacing: 2rpx;
+ }
+ }
+
+ /* 可拖动悬浮球 */
+ .float-ball {
+ position: fixed;
+ width: 120rpx;
+ height: 120rpx;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow:
+ 0 8rpx 24rpx rgba(82, 196, 26, 0.45),
+ 0 16rpx 40rpx rgba(0, 0, 0, 0.15),
+ inset 0 4rpx 10rpx rgba(255, 255, 255, 0.35);
+ z-index: 999;
+ touch-action: none;
+ transition: transform 0.2s ease;
+
+ &:active {
+ transform: scale(0.92);
+ }
+
+ .ball-shine {
+ position: absolute;
+ top: 20rpx;
+ left: 24rpx;
+ width: 36rpx;
+ height: 18rpx;
+ background: rgba(255, 255, 255, 0.4);
+ border-radius: 50%;
+ filter: blur(2rpx);
+ pointer-events: none;
+ }
+
+ .ball-text {
+ color: #fff;
+ font-size: 28rpx;
+ font-weight: 700;
+ letter-spacing: 2rpx;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.15);
position: relative;
z-index: 1;
- text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
}
}
diff --git a/src/static/icon/video.mp3 b/src/static/icon/video.mp3
new file mode 100644
index 0000000..f6c6e22
Binary files /dev/null and b/src/static/icon/video.mp3 differ