feat: 接入AI阳光跑页面与年级班级级联筛选
新增阳光跑路由和列表页面,按学生姓名、年级班级及日期查询记录并支持分页。 查询与 Excel 导出共用所选年级、班级 ID,保留学校后台的页面风格。
This commit is contained in:
parent
eedc88e59f
commit
4e77cfeed2
24
src/api/sunshineRun.js
Normal file
24
src/api/sunshineRun.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import http from './http'
|
||||||
|
import store from '@/store/index'
|
||||||
|
|
||||||
|
function queryString(params) {
|
||||||
|
const query = new URLSearchParams()
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== null && value !== undefined && value !== '') {
|
||||||
|
query.append(key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return query.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSunshineRunPage(params) {
|
||||||
|
return http.get(`/api/SunshineRun/GetPageList?${queryString(params)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportSunshineRun(params) {
|
||||||
|
return axios.get(`/api/SunshineRun/Export?${queryString(params)}`, {
|
||||||
|
responseType: 'blob',
|
||||||
|
headers: { Authorization: store.getters.getToken() }
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -428,6 +428,14 @@ const routes = [{
|
|||||||
keepAlive: false
|
keepAlive: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/sunshineRun',
|
||||||
|
name: 'sunshineRun',
|
||||||
|
component: () => import('@/views/motionData/sunshineRun/index.vue'),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/trainingDetail', //运动数据/训练数据/详情
|
path: '/trainingDetail', //运动数据/训练数据/详情
|
||||||
name: 'trainingDetail',
|
name: 'trainingDetail',
|
||||||
|
|||||||
@ -810,6 +810,7 @@ export default defineComponent({
|
|||||||
'/userActivity': 'el-icon-s-marketing',
|
'/userActivity': 'el-icon-s-marketing',
|
||||||
'/physicalTestData': 'el-icon-s-data',
|
'/physicalTestData': 'el-icon-s-data',
|
||||||
'/trainingData': 'el-icon-s-data',
|
'/trainingData': 'el-icon-s-data',
|
||||||
|
'/sunshineRun': 'el-icon-s-data',
|
||||||
'/visionData': 'el-icon-s-check',
|
'/visionData': 'el-icon-s-check',
|
||||||
'/specialManagement': 'el-icon-s-management',
|
'/specialManagement': 'el-icon-s-management',
|
||||||
'/ropeJumping': 'el-icon-s-cooperation',
|
'/ropeJumping': 'el-icon-s-cooperation',
|
||||||
|
|||||||
284
src/views/motionData/sunshineRun/index.vue
Normal file
284
src/views/motionData/sunshineRun/index.vue
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sunshine-run">
|
||||||
|
<commonManage>
|
||||||
|
<div class="head">
|
||||||
|
<div class="left">
|
||||||
|
<p class="desctipt">姓名:</p>
|
||||||
|
<el-input v-model.trim="filters.studentName" class="m-2" clearable
|
||||||
|
placeholder="请输入姓名" @keyup.enter="search" />
|
||||||
|
</div>
|
||||||
|
<div class="left">
|
||||||
|
<p class="desctipt">班级:</p>
|
||||||
|
<el-cascader v-model="filters.gradeClass" class="grade-class-cascader"
|
||||||
|
:options="gradeClassOptions" :props="cascaderProps" clearable filterable
|
||||||
|
placeholder="请选择年级/班级" />
|
||||||
|
</div>
|
||||||
|
<div class="left">
|
||||||
|
<p class="desctipt">起始日期:</p>
|
||||||
|
<el-date-picker v-model="dateRange" class="date-range" type="daterange"
|
||||||
|
format="YYYY-MM-DD" value-format="YYYY-MM-DD" range-separator="至"
|
||||||
|
start-placeholder="开始日期" end-placeholder="结束日期" clearable />
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<el-button class="Search" :icon="Search" @click="search">查询</el-button>
|
||||||
|
<el-button class="Audit" :icon="Refresh" @click="reset">重置</el-button>
|
||||||
|
<el-button class="Export" :icon="Download" :loading="exporting" @click="exportData">导出</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cont">
|
||||||
|
<el-table v-loading="loading" :data="records" class="record-table"
|
||||||
|
:header-cell-style="headerCellStyle" :cell-style="cellStyle"
|
||||||
|
:row-style="{ height: '0px' }">
|
||||||
|
<el-table-column fixed prop="index" label="序号" width="60" />
|
||||||
|
<el-table-column prop="studentName" label="姓名" min-width="115" />
|
||||||
|
<el-table-column prop="sexName" label="性别" width="75" />
|
||||||
|
<el-table-column prop="className" label="班级" min-width="150" />
|
||||||
|
<el-table-column prop="effectiveDistance" label="有效里程" min-width="110" />
|
||||||
|
<el-table-column prop="effectiveTime" label="有效用时" min-width="110" />
|
||||||
|
<el-table-column prop="totalDistance" label="总里程" min-width="105" />
|
||||||
|
<el-table-column prop="totalTime" label="总用时" min-width="105" />
|
||||||
|
<el-table-column prop="startTime" label="开始时间" min-width="160" />
|
||||||
|
<el-table-column prop="endTime" label="结束时间" min-width="160" />
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无阳光跑数据" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
v-model:current-page="pageIndex"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 50, 100, 200]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="total"
|
||||||
|
@size-change="changePageSize"
|
||||||
|
@current-change="loadRecords"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</commonManage>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
import { Download, Refresh, Search } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import commonManage from '@/components/commonManage/index.vue'
|
||||||
|
import { GetGradeNames } from '@/api/grade'
|
||||||
|
import { exportSunshineRun, getSunshineRunPage } from '@/api/sunshineRun'
|
||||||
|
|
||||||
|
const filters = reactive({ studentName: '', gradeClass: [] })
|
||||||
|
const gradeClassOptions = ref([])
|
||||||
|
const cascaderProps = { checkStrictly: true }
|
||||||
|
const dateRange = ref([])
|
||||||
|
const appliedFilters = reactive({ studentName: '', gradeId: '', classId: '', startDate: '', endDate: '' })
|
||||||
|
const records = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const exporting = ref(false)
|
||||||
|
const pageIndex = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const headerCellStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#3F404A',
|
||||||
|
padding: '3px',
|
||||||
|
background: '#F8F8FA',
|
||||||
|
height: '50px'
|
||||||
|
}
|
||||||
|
const cellStyle = {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#3F404A',
|
||||||
|
padding: '3px'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRecords() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const response = await getSunshineRunPage({
|
||||||
|
...appliedFilters,
|
||||||
|
pageIndex: pageIndex.value,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
})
|
||||||
|
if (response.code !== 200) throw new Error(response.message || '阳光跑数据加载失败')
|
||||||
|
records.value = response.data?.datas || []
|
||||||
|
total.value = response.data?.total || 0
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error?.message || '阳光跑数据加载失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilterParams() {
|
||||||
|
return {
|
||||||
|
studentName: filters.studentName,
|
||||||
|
gradeId: filters.gradeClass?.[0] || '',
|
||||||
|
classId: filters.gradeClass?.[1] || '',
|
||||||
|
startDate: dateRange.value?.[0] || '',
|
||||||
|
endDate: dateRange.value?.[1] || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadGradeClassOptions() {
|
||||||
|
try {
|
||||||
|
const response = await GetGradeNames()
|
||||||
|
if (response.code !== 200) throw new Error(response.message || '年级班级加载失败')
|
||||||
|
gradeClassOptions.value = (response.data || []).map(grade => ({
|
||||||
|
value: grade.id,
|
||||||
|
label: grade.gradeName,
|
||||||
|
children: (grade.class || []).map(classroom => ({
|
||||||
|
value: classroom.id,
|
||||||
|
label: classroom.className
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error?.message || '年级班级加载失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
Object.assign(appliedFilters, getFilterParams())
|
||||||
|
pageIndex.value = 1
|
||||||
|
loadRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
Object.assign(filters, { studentName: '', gradeClass: [] })
|
||||||
|
dateRange.value = []
|
||||||
|
pageSize.value = 10
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePageSize() {
|
||||||
|
pageIndex.value = 1
|
||||||
|
loadRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportData() {
|
||||||
|
exporting.value = true
|
||||||
|
try {
|
||||||
|
const response = await exportSunshineRun(getFilterParams())
|
||||||
|
const blob = response.data
|
||||||
|
if (!blob || blob.type?.includes('json')) throw new Error('导出失败')
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `AI阳光跑${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
link.remove()
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 1000)
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error?.message || '导出失败')
|
||||||
|
} finally {
|
||||||
|
exporting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadGradeClassOptions()
|
||||||
|
loadRecords()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.sunshine-run {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.head {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 72px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
width: auto;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.desctipt {
|
||||||
|
width: auto;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: PingFangSC;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #3f404a;
|
||||||
|
margin-left: 20px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-2 {
|
||||||
|
width: 200px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-class-cascader {
|
||||||
|
width: 220px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-range {
|
||||||
|
width: 330px !important;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
width: auto;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-left: auto;
|
||||||
|
gap: 10px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cont {
|
||||||
|
width: 100%;
|
||||||
|
min-height: calc(100vh - 200px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.record-table {
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
height: calc(100vh - 270px);
|
||||||
|
min-height: 360px;
|
||||||
|
margin: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.sunshine-run {
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.head .left {
|
||||||
|
width: 100%;
|
||||||
|
.desctipt { margin-left: 0; }
|
||||||
|
.m-2 { flex: 1; margin-left: 8px; }
|
||||||
|
.grade-class-cascader { flex: 1; width: auto; min-width: 0; margin-left: 8px; }
|
||||||
|
.date-range { width: auto !important; flex: 1; min-width: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.head .right {
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 0;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cont .pagination {
|
||||||
|
overflow-x: auto;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user