2026-04-28 14:20:44 +08:00

340 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// pages/parent/report/report.js
const tabService = require("../../../utils/tab-serve");
import uCharts from '../../../utils/chart/u-charts';
import {
getReport,
getPhysicalFitnessReport
} from '../../../utils/serve/parent'
import { getClassRoomRecord } from '../../../utils/serve/teacher';
import { getWeekdayWithValidation } from '../../../utils/utils';
var uChartsInstance = {};
// 各指标单位映射(接口不返回单位)
const UNIT_MAP = {
'BMI': 'kg/m²',
'肺活量': 'ml',
'坐位体前屈': 'cm',
'立定跳远': 'cm',
'一分钟跳绳': '个',
'一分钟仰卧起坐': '个',
};
Page({
/**
* 页面的初始数据
*/
data: {
report: {},
list: [],
isTrainee: false,
pageIndex: 1,
hasMore: true,
roomList: [],
// 体测报告
activeTab: 0, // 0=课堂报告 1=体测报告
fitnessItems: [], // 六个指标卡片
fitnessReport: null, // 接口返回的完整体测数据
radarCWidth: 300,
radarCHeight: 300,
pixelRatio: 2,
showFitnessPopup: false, // 成绩弹窗
selectedFitnessItem: null,
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
if(wx.getStorageSync('roleId') && wx.getStorageSync('roleId') == 5 ){
this.setData({
isTrainee: true
}, () => {
this.getTrainClass(true)
this.fetchFitnessReport()
})
} else {
await this.getStudentReport()
}
},
async getStudentReport() {
const res = await getReport()
let list = []
res.data.categoryScoreList.forEach(e => {
list.push({
...e,
width: e.score >= 60 && e.score <= 80 ? (((e.score - 60) / 4)*5 + 25) + '%' : ((e.score - 60) / 0.4 + 2) + '%',
grade: e.score >= 90 ? 'grate' : e.score >= 80 ? 'good' : e.score >= 60 ? 'normal' : ''
})
})
res.data.width = res.data.totalScore >= 60 && res.data.totalScore <= 80 ? (((res.data.totalScore - 60) / 4)*5 + 25) + '%' : ((res.data.totalScore - 60) / 0.4 + 2) + '%',
res.data.grade = res.data.totalScore >= 90 ? 'grate' : res.data.totalScore >= 80 ? 'good' : res.data.totalScore >= 60 ? 'normal' : ''
res.data.totalScore = res.data.totalScore.toFixed(0)
res.data.bodyFunction = res.data.bodyFunction.toFixed(0)
res.data.bodyQuality = res.data.bodyQuality.toFixed(0)
res.data.bodyShape = res.data.bodyShape.toFixed(0)
this.setData({
report: res.data,
list
})
},
// 切换体测/课堂 Tab
switchTab(e) {
const tab = Number(e.currentTarget.dataset.tab)
if (tab === this.data.activeTab) return
this.setData({ activeTab: tab }, () => {
if (tab === 1) {
// 切到体测报告时绘制雷达图
this.drawRadarChart(this)
}
})
},
// 获取体测报告数据
async fetchFitnessReport() {
try {
const res = await getPhysicalFitnessReport()
const rawList = (res.data && res.data.categoryScoreList) || []
const list = rawList.filter(item => !(item.name && item.name.includes('米') && item.name.includes('跑')))
// 雷达图根据 gradeId 过滤不适用的项目
const gradeId = list.length > 0 ? list[0].gradeId : null
let radarList = list
if (gradeId !== null && gradeId !== undefined) {
if (gradeId <= 2) {
radarList = list.filter(item => item.name !== '一分钟仰卧起坐' && item.name !== '立定跳远')
} else if (gradeId > 2 && gradeId <= 6) {
radarList = list.filter(item => item.name !== '立定跳远')
} else if (gradeId > 6) {
radarList = list.filter(item => item.name !== '一分钟跳绳')
}
}
const fitnessItems = list.map(item => ({
...item,
unit: UNIT_MAP[item.name] || '',
}))
this.setData({
fitnessReport: radarList,
fitnessItems,
})
} catch (e) {
this.setData({ fitnessItems: [] })
}
},
// 绘制六边形雷达图
drawRadarChart(that) {
const list = that.data.fitnessReport || []
// 长文本自动换行(超过5字在中间插入换行)
const categories = list.map(i => {
const name = i.name || ''
if (name.length > 5) {
const mid = Math.ceil(name.length / 2)
return name.slice(0, mid) + '\n' + name.slice(mid)
}
return name
})
const prevData = list.map(i => i.prevScore || 0) // 上次成绩
const currentData = list.map(i => i.lastScore || 0) // 本次成绩
// 本次成绩数据标签:显示分数 + 等级
const currentDataLabels = list.map(i => {
const score = i.lastScore != null ? i.lastScore : ''
const rank = i.rank != null ? i.rank : ''
return rank ? score + '\n' + rank : String(score)
})
const query = wx.createSelectorQuery().in(that)
query.select('#radarCanvas').fields({ node: true, size: true }).exec(res => {
if (!res[0]) return
const canvas = res[0].node
const ctx = canvas.getContext('2d')
canvas.width = (res[0].width + 20) * that.data.pixelRatio
canvas.height = (res[0].height + 20) * that.data.pixelRatio
uChartsInstance['radarCanvas'] = new uCharts({
type: 'radar',
context: ctx,
width: that.data.radarCWidth * that.data.pixelRatio,
height: that.data.radarCHeight * that.data.pixelRatio,
categories,
series: [
{ name: '上次成绩', data: prevData, color: '#4A90E2', dataLabel: false },
{ name: '本次成绩', data: currentData, color: '#E74C3C', dataLabels: currentDataLabels, textColor: '#E74C3C', textSize: 11 },
],
pixelRatio: that.data.pixelRatio,
animation: true,
tapLegend:true,
background: 'rgba(0,0,0,0)',
color: ['#4A90E2', '#E74C3C'],
padding: [0, 0, 0, 15],
legend: {
show: true,
position: 'bottom',
float: 'center',
fontSize: 12,
lineHeight: 11,
margin: 0,
itemGap: 20,
},
extra: {
radar: {
gridType: this.data.fitnessReport.length == 4 ? 'circle' : 'radar',
gridColor: '#CCCCCC',
gridCount: 4,
opacity: 0.15,
gridEvenShow: true,
gridOddShow: true,
labelShow: true,
labelColor: '#333',
fontSize: 13,
labelMargin: 30,
labelPointShow: false,
max: 100,
},
},
})
})
},
// 点击图标:展示成绩弹窗
showFitnessScore(e) {
const item = e.currentTarget.dataset.item
this.setData({ selectedFitnessItem: item, showFitnessPopup: true })
},
// 关闭成绩弹窗
closeFitnessPopup() {
this.setData({ showFitnessPopup: false, selectedFitnessItem: null })
},
// 弹窗内"查看详情"
goFitnessDetail() {
const item = this.data.selectedFitnessItem
if (!item) return
this.setData({ showFitnessPopup: false })
wx.setStorageSync('fitnessDetailItem', item)
wx.navigateTo({
url: '/subpackage/parent/fitness-detail/fitness-detail',
})
},
//获取教练课堂记录 刷新 加载更多
async getTrainClass(refresh) {
if (!this.data.hasMore && !refresh) return //非刷新且没有
const res = await getClassRoomRecord({
pageIndex: this.data.pageIndex,
PageSize: 20,
})
let newList = res.data.datas.map(item => {
return {
...item,
time: getWeekdayWithValidation(Number(item.startingEndingTime.slice(0, 4)), Number(item.startingEndingTime.slice(5, 7)), Number(item.startingEndingTime.slice(8, 10)))
}
})
this.setData({
roomList: refresh ? newList : [...this.data.roomList, ...newList],
page: this.data.page + 1,
hasMore: res.data.total > (this.data.pageIndex) * 20,
})
if(this.data.isTrainee && this.data.activeTab === 1){
this.drawRadarChart(this)
}
},
//查看报告详情
goReportDetail(e){
wx.navigateTo({
url: '/subpackage/teacher/report-detail/report-detail?id='+e.currentTarget.id ,
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
const windowInfo = wx.getWindowInfo()
const pixelRatio = windowInfo.pixelRatio
// 宽度 = 屏幕宽度 - 页面两侧 padding(30rpx×2),不计 card padding(由负 margin 抵消)
const rpxRatio = windowInfo.windowWidth / 750
const radarCWidth = Math.floor(windowInfo.windowWidth - 2 * 30 * rpxRatio)
const radarCHeight = Math.floor(radarCWidth * 1.05) // 稍高,给换行标签和 rank 留出空间
this.setData({ radarCWidth, radarCHeight, pixelRatio }, () => {
if (this.data.isTrainee && this.data.activeTab === 1) {
this.drawRadarChart(this)
}
})
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
tabService.updateIndex(this, 2)
if(wx.getStorageSync('roleId') && wx.getStorageSync('roleId') == 5){ //学员
this.setData({
isTrainee:true
})
}
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
async onPullDownRefresh() {
if(this.data.isTrainee){
this.setData({
pageIndex:1,
hasMore:true
},async ()=>{
this.getTrainClass(true)
await this.fetchFitnessReport()
wx.stopPullDownRefresh()
wx.showToast({
title: '刷新成功',
icon: 'none'
})
})
}else{
await this.getStudentReport()
wx.stopPullDownRefresh()
wx.showToast({
title: '刷新成功!',
icon: 'none'
})
}
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
this.getTrainClass()
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})