import { defineComponent, reactive, ref, shallowReactive } from 'vue' import styles from './index.module.less' import iconArrow1 from '../images/icon-arrow1.png' import iconArrow11 from '../images/icon-arrow1-1.png' import icon1 from '../images/icon-1.png' import icon2 from '../images/icon-2.png' import ArrowUp from '../images/arrow-up.png' import ArrowUpActive from '../images/arrow-up-active.png' import iconDownload from '../images/icon-download.png' import { Button, DatetimePicker, Popup, Toast } from 'vant' import Echats from './echats' import ColHeader from '@/components/col-header' import TheSticky from '@/components/the-sticky' import { formatterDatePicker } from '@/helpers/utils' import dayjs from 'dayjs' import request from '@/helpers/request' import { getTimeRange, TIME_TYPE } from '../home-statistics' import ColResult from '@/components/col-result' import { promisefiyPostMessage } from '@/helpers/native-message' import { useRouter } from 'vue-router' import { state } from '@/state' /** 秒转分 */ export const formatSecToMin = (second: number) => { if (isNaN(second)) { return '0' } const mm = (Math.floor(second / 60) + Math.floor(second % 60) / 60).toFixed(2) return mm } /** 秒转时分秒 */ export const formatSecToHMS = second => { const hours = Math.floor(second / 3600) .toString() .padStart(2, '0') const minutes = Math.floor((second % 3600) / 60) .toString() .padStart(2, '0') const seconds = Math.round(second % 60) .toString() .padStart(2, '0') return { all: hours + '时' + minutes + '分' + seconds + '秒', hours, minutes, seconds } } const catchKey = 'practice-statistics-detail-search' export default defineComponent({ name: 'PracticeDetail', setup() { const router = useRouter() let catchSearch: any = sessionStorage.getItem(catchKey) catchSearch = catchSearch ? JSON.parse(catchSearch) : {} sessionStorage.removeItem(catchKey) const searchStatus = ref(false) const currentType = ref( catchSearch.currentType !== undefined ? catchSearch.currentType : 'MONTH' ) const searchObj = reactive({ tempSubjectId: catchSearch.subjectId || ('' as any), type: catchSearch.currentType !== undefined ? catchSearch.currentType : ('MONTH' as TIME_TYPE) }) const timeRange = catchSearch.startTime && catchSearch.endTime ? { startTime: catchSearch.startTime, endTime: catchSearch.endTime } : getTimeRange(currentType.value) const forms = reactive({ loading: false, dataShow: true, isScrollLeft: false, subjectId: catchSearch.subjectId || ('' as any), // 选择的声部 subjectList: [] as any, startTimeStatus: false, startTimeClosedStatus: false, endTimeMinDate: new Date(timeRange?.startTime || ''), endTimeMaxDate: dayjs(new Date(timeRange?.startTime || '')) .add(1, 'year') .toDate(), endTimeStatus: false, endTimeClosedStatus: false, startTime: new Date(timeRange?.startTime || ''), startTimeStr: timeRange?.startTime || '', endTime: new Date(timeRange?.endTime || ''), endTimeStr: timeRange?.endTime || '', sortField: '' as 'totalPracticeTime' | 'averagePracticeTime' | '', // 排序字段 sortType: '' as 'ASC' | 'DESC' | '' // 排序方式 ,ASC升序,DESC降序 }) // 练习统计 const practiceSummary = shallowReactive({ averagePracticeTime: '0', practiceCount: '0', totalPracticeTime: '0', totalTimes: { hours: '00', minutes: '00', seconds: '00' } }) const obj = ref({ students: [] as any, xAxisDataTime: [] as any, yAxisDataTime: [] as any, timeMaxCount: 5, timeCount: 0 as any, timeStr: '', xAxisDataCount: [] as any, yAxisDataCount: [] as any, countMaxCount: 5, // 默认横线 countCount: 0, countStr: '' }) // const searchText = computed(() => { // const template = { // MONTH: '本月', // THREE_MONTH: '近三个月', // HALF_YEAR: '近年半', // YEAR: '近一年' // } // return template[currentType.value] // }) // 导出学生练习时长数据 const onExport = async () => { try { const { data } = await request.post( '/api-teacher/home/exportStudentPractice', { data: { startTime: forms.startTimeStr, endTime: forms.endTimeStr, subjectId: forms.subjectId, sortField: forms.sortField, sortType: forms.sortType // 排序方式 ,ASC升序,DESC降序 } } ) const pathname = data || '' if (pathname) { const fileName = `练习详情${forms.startTimeStr}~${forms.endTimeStr}_${state.user.data?.userId}` // 发送消息通知移动端下载文件 promisefiyPostMessage({ api: 'downloadFile', content: { downloadUrl: pathname, fileName } }) } } catch { // } } const getDetail = async () => { forms.loading = true try { const { data } = await request.post('/api-teacher/home/practice', { data: { startTime: forms.startTimeStr, endTime: forms.endTimeStr, subjectId: forms.subjectId } }) const summary = data.practiceSummary || {} practiceSummary.averagePracticeTime = formatSecToMin( summary.averagePracticeTime || 0 ) practiceSummary.practiceCount = summary.practiceCount || 0 practiceSummary.totalPracticeTime = summary.totalPracticeTime || 0 practiceSummary.totalTimes = formatSecToHMS( summary.totalPracticeTime || 0 ) // 练习时长 const practiceTimes = data.practiceTimes || [] const xAxisDataTimes: string[] = [] const practiceTimeList: any[] = [] practiceTimes.forEach((item: any, index: number) => { xAxisDataTimes.push(item.date) practiceTimeList.push([ index, formatSecToMin(item.practiceTime), item.practiceTime ]) if (practiceTimes.length - 1 === index) { obj.value.timeCount = item.practiceTime obj.value.timeStr = item.date } }) // 练习人数 const practiceCounts = data.practiceCounts || [] const xAxisDataCounts: string[] = [] const countList: number[] = [] let maxCount = 0 // 最大人数 - 用记设置练习人数分割线 practiceCounts.forEach((item: any, index: number) => { xAxisDataCounts.push(item.date) countList.push(item.practiceTime) if (maxCount < (item.practiceTime || 0)) { maxCount = item.practiceTime } if (practiceCounts.length - 1 === index) { obj.value.countCount = item.practiceTime obj.value.countStr = item.date } }) obj.value.xAxisDataTime = xAxisDataTimes obj.value.yAxisDataTime = practiceTimeList obj.value.xAxisDataCount = xAxisDataCounts obj.value.yAxisDataCount = countList // 最小数量为1 obj.value.countMaxCount = maxCount >= 5 ? 5 : Math.max(maxCount, 1) } catch { // } forms.loading = false } // 用户列表数据 const getStudentDetail = async () => { try { const { data } = await request.post( '/api-teacher/home/studentPractice', { data: { startTime: forms.startTimeStr, endTime: forms.endTimeStr, subjectId: forms.subjectId, sortField: forms.sortField, sortType: forms.sortType } } ) // 学员练习时长 const studentPracticeSummary = data || [] const tempStudents: any = [] studentPracticeSummary.forEach((item: any) => { const student = { avatar: item.avatar, averagePracticeTime: formatSecToHMS(item.averagePracticeTime || 0), practiceDays: item.practiceDays || 0, studentName: item.studentName, subjectName: item.subjectName, totalPracticeTime: formatSecToHMS(item.totalPracticeTime || 0), userId: item.userId } tempStudents.push(student) }) obj.value.students = tempStudents forms.dataShow = tempStudents.length > 0 ? true : false } catch { // } } const getSubjectList = async () => { const { data } = await request.get( `/api-teacher/subject/subSubjectSelect?type=MUSIC` ) if (Array.isArray(data)) { forms.subjectList = data } } getSubjectList() getDetail() getStudentDetail() const onChangeTime = (type: TIME_TYPE) => { if (searchObj.type === type) return searchObj.type = type resetTime(type) } // 格式化 const resetTime = (type: TIME_TYPE) => { const timeRang = getTimeRange(type) forms.startTime = new Date(timeRang?.startTime || '') forms.startTimeStr = timeRang?.startTime || '' forms.endTimeMinDate = dayjs(timeRang?.startTime || '').toDate() forms.endTimeMaxDate = dayjs(timeRang?.startTime || '') .add(1, 'year') .toDate() forms.endTime = new Date(timeRang?.endTime || '') forms.endTimeStr = timeRang?.endTime || '' } // 重置 const onConfirm = () => { if (!forms.startTimeStr || !forms.endTimeStr) { Toast('请选择时间范围') return } // timeRange.value = getTimeRange(currentType.value) searchStatus.value = false forms.subjectId = searchObj.tempSubjectId currentType.value = searchObj.type getDetail() getStudentDetail() } /** 排序 */ const onSort = ( field: 'totalPracticeTime' | 'averagePracticeTime' | '' ) => { if (!field) return if (forms.sortField !== field) { forms.sortType = '' } forms.sortField = field if (forms.sortType === 'ASC') { forms.sortType = '' forms.sortField = '' } else if (forms.sortType === 'DESC') { forms.sortType = 'ASC' } else { forms.sortType = 'DESC' } getStudentDetail() } /** 跳转详情 */ const toDetail = (item: any) => { sessionStorage.setItem( catchKey, JSON.stringify({ startTime: forms.startTimeStr, endTime: forms.endTimeStr, currentType: currentType.value, subjectId: forms.subjectId }) ) router.push({ path: '/exercise-detail', query: { studentId: item.userId || '' } }) } return () => (
(searchStatus.value = true)} > 筛选
总练习时长
总练习时长
{practiceSummary.totalTimes.hours} 小时 {practiceSummary.totalTimes.minutes} {practiceSummary.totalTimes.seconds}
练习人数
{practiceSummary.practiceCount}
{/*
平均练习时长
{practiceSummary.averagePracticeTime} 分钟
*/}
练习时长
练习人数
学员练习详情
导出
{forms.dataShow ? ( {obj.value.students.map((item: any) => ( toDetail(item)}> ))}
学员 乐器
onSort('totalPracticeTime')} > 练习总时长
练习天数
onSort('averagePracticeTime')} > 平均练习时长
{item.studentName} {item.subjectName} {item.totalPracticeTime.hours}小时 {item.totalPracticeTime.minutes}分 {item.totalPracticeTime.seconds}秒 {item.practiceDays} {item.averagePracticeTime.hours}小时 {item.averagePracticeTime.minutes}分 {item.averagePracticeTime.seconds}秒
) : ( )}
筛选
时间

onChangeTime('MONTH')} class={searchObj.type === 'MONTH' ? styles.active : ''} > 本月

onChangeTime('THREE_MONTH')} class={ searchObj.type === 'THREE_MONTH' ? styles.active : '' } > 近三个月

onChangeTime('HALF_YEAR')} class={searchObj.type === 'HALF_YEAR' ? styles.active : ''} > 近半年

onChangeTime('YEAR')} class={searchObj.type === 'YEAR' ? styles.active : ''} > 近一年

{ forms.startTimeStatus = true forms.startTimeClosedStatus = true }} > {forms.startTimeStr || '起始时间'}

{ forms.endTimeStatus = true forms.endTimeClosedStatus = true }} > {forms.endTimeStr || '终止时间'}

声部

(searchObj.tempSubjectId = '')} > 全部

{forms.subjectList.map((item: any) => (

{ searchObj.tempSubjectId = item.id }} > {item.name}

))}
{/* 开始日期 */} { forms.startTimeClosedStatus = false }} > {forms.startTimeClosedStatus && ( (forms.startTimeStatus = false)} onConfirm={(val: any) => { forms.startTime = val forms.startTimeStr = dayjs(val).format('YYYY-MM-DD') forms.startTimeStatus = false forms.endTimeMinDate = dayjs(val || new Date()).toDate() forms.endTimeMaxDate = dayjs(val || new Date()) .add(1, 'year') .toDate() forms.endTime = val forms.endTimeStr = '' searchObj.type = '' as any }} /> )} {/* 结束日期 */} { forms.endTimeClosedStatus = false }} > {forms.endTimeClosedStatus && ( (forms.endTimeStatus = false)} onConfirm={(val: any) => { forms.endTime = val forms.endTimeStatus = false forms.endTimeStr = dayjs(val).format('YYYY-MM-DD') searchObj.type = '' as any }} /> )}
) } })