181 lines
5.7 KiB
JavaScript
181 lines
5.7 KiB
JavaScript
// subpackage/teacher/student-fitness-detail/student-fitness-detail.js
|
||
import uCharts from '../../../utils/chart/u-charts';
|
||
import { getReport } from '../../../utils/serve/parent';
|
||
|
||
var uChartsInstance = {};
|
||
|
||
const UNIT_MAP = {
|
||
'BMI': 'kg/m²',
|
||
'肺活量': 'ml',
|
||
'坐位体前屈': 'cm',
|
||
'立定跳远': 'cm',
|
||
'一分钟跳绳': '个',
|
||
'一分钟仰卧起坐': '个',
|
||
};
|
||
|
||
Page({
|
||
data: {
|
||
fitnessItems: [],
|
||
fitnessReport: [],
|
||
radarCWidth: 300,
|
||
radarCHeight: 300,
|
||
pixelRatio: 2,
|
||
showFitnessPopup: false,
|
||
selectedFitnessItem: null,
|
||
studentNo: '',
|
||
readyFired: false,
|
||
},
|
||
|
||
async onLoad(options) {
|
||
wx.showLoading({ title: '加载中' });
|
||
this.setData({ studentNo: options.studentNo || '' });
|
||
try {
|
||
const res = await getReport({ studentNo: options.studentNo });
|
||
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,
|
||
}, () => {
|
||
// 如果 onReady 已经触发,数据到位后立即绘图
|
||
if (this.data.readyFired) {
|
||
this.drawRadarChart(this);
|
||
}
|
||
});
|
||
} catch (e) {
|
||
this.setData({ fitnessItems: [] });
|
||
}
|
||
wx.hideLoading();
|
||
},
|
||
|
||
onReady() {
|
||
const info = wx.getWindowInfo();
|
||
const pixelRatio = info.pixelRatio;
|
||
const rpxRatio = info.windowWidth / 750;
|
||
const radarCWidth = Math.floor(info.windowWidth - 2 * 30 * rpxRatio);
|
||
const radarCHeight = Math.floor(radarCWidth * 1.05);
|
||
this.setData({ radarCWidth, radarCHeight, pixelRatio, readyFired: true }, () => {
|
||
// 如果数据已经加载完,直接绘图;否则等 onLoad 回调里触发
|
||
if (this.data.fitnessReport && this.data.fitnessReport.length > 0) {
|
||
this.drawRadarChart(this);
|
||
}
|
||
});
|
||
},
|
||
|
||
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: that.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',
|
||
});
|
||
},
|
||
|
||
onShow() {},
|
||
onHide() {},
|
||
onUnload() {},
|
||
onPullDownRefresh() {},
|
||
onReachBottom() {},
|
||
onShareAppMessage() {},
|
||
})
|