123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue';
- import styles from './index.module.less';
- import MSticky from '@/components/m-sticky';
- import MHeader from '@/components/m-header';
- import { browser, formatterDatePicker } from '@/helpers/utils';
- import { useRouter } from 'vue-router';
- import { DatePicker, List, Popup, Tab, Tabs } from 'vant';
- import { postMessage } from '@/helpers/native-message';
- import { listenerMessage } from '@/helpers/native-message';
- import OFullRefresh from '@/components/m-full-refresh';
- import iconBird from './images/icon-bird.png';
- import iconPP from './images/icon-p-p.png';
- import iconNumber from './images/icon-number.png';
- import iconAlbum from './images/icon-album.png';
- import icon1 from './images/icon-1.png';
- import icon2 from './images/icon-2.png';
- import iconArrow from './images/icon-arrow.png';
- import iconArrowActive from './images/icon-arrow-active.png';
- import iconLl from './images/l-l.png';
- import iconLc from './images/l-c.png';
- import iconLr from './images/l-r.png';
- import iconPl from './images/p-l.png';
- import iconPc from './images/p-c.png';
- import iconPr from './images/p-r.png';
- import MSearch from '@/components/m-search';
- import TheTimeRange from '@/components/the-time-range';
- import request from '@/helpers/request';
- import DetailItem from './modals/detail-item';
- import MEmpty from '@/components/m-empty';
- import dayjs from 'dayjs';
- export default defineComponent({
- name: 'exercise-record-index',
- setup() {
- const router = useRouter();
- const tabsRef = ref();
- const state = reactive({
- tabActive: 'EVALUATION' as 'EVALUATION' | 'PRACTICE',
- isClick: false,
- showPopoverTime: false
- });
-
-
- const formEvaluation = reactive({
- startTime: '',
- endTime: '',
- musicSheetName: null,
- page: 1,
- rows: 20
- });
- const fromPractice = reactive({
- showData: true,
- showTime: false,
- currentDate: [dayjs().format('YYYY'), dayjs().format('MM')],
- practiceMonthName:
- dayjs().format('YYYY') + '年' + dayjs().format('MM') + '月',
- practiceDetail: {} as any,
- practiceList: [] as any
- });
- const refreshing = ref(false);
- const loading = ref(false);
- const finished = ref(false);
- const showContact = ref(true);
- const topWrapHeight = ref(0);
- const infoDetail = ref({
- evaluationNum: 0,
- userMusicNum: 0
- });
- const onRefresh = () => {
- finished.value = false;
-
-
- loading.value = true;
- getList();
- };
- const list = ref([]);
- const getList = async () => {
- if (state.isClick) {
- return;
- }
- state.isClick = true;
- if (refreshing.value) {
- list.value = [];
- formEvaluation.page = 1;
- refreshing.value = false;
- }
- try {
- const res = await request.post(`/edu-app/musicPracticeRecord/page`, {
- data: { ...formEvaluation, feature: 'EVALUATION' }
- });
- if (list.value.length > 0 && res.data.current === 1) {
- return;
- }
- list.value = list.value.concat(res.data.rows || []);
- formEvaluation.page = res.data.current + 1;
- showContact.value = list.value.length > 0;
- loading.value = false;
- finished.value = res.data.current >= res.data.pages;
- } catch {
- showContact.value = false;
- finished.value = true;
- }
- state.isClick = false;
- };
- const getDetail = async () => {
- try {
- const { data } = await request.post(
- `/edu-app/musicPracticeRecord/studentStat`,
- {
- data: {
- startTime: formEvaluation.startTime,
- endTime: formEvaluation.endTime,
- musicSheetName: formEvaluation.musicSheetName
- }
- }
- );
- infoDetail.value = { ...data };
- } catch (e: any) {}
- };
- const onEvaluation = () => {
- getDetail();
- getList();
- };
- const tabResize = () => {
- tabsRef.value?.resize();
- };
-
- const getPractice = async () => {
- try {
- const currentDate = fromPractice.currentDate.join('-');
- const { data } = await request.post(
- `/edu-app/musicPracticeRecord/studentTrainStat`,
- {
- data: {
- startTime: currentDate + '-01 00:00:00',
- endTime:
- dayjs(currentDate).endOf('month').format('YYYY-MM-DD') +
- ' 23:59:59'
- }
- }
- );
- const { studentTrainStatList, ...more } = data;
- fromPractice.showData = studentTrainStatList?.length > 0;
- fromPractice.practiceDetail = { ...more };
- const tempList: any = [];
- let maxTime = 0;
- studentTrainStatList?.forEach((item: any) => {
- if (item.practiceTimes > maxTime) {
- maxTime = item.practiceTimes;
- }
- });
- studentTrainStatList?.forEach((item: any) => {
- tempList.push({
- date: dayjs(item.practiceDate).format('MM/DD'),
- time: parseFloat((item.practiceTimes / 60).toFixed(2)),
- rate: Math.floor((item.practiceTimes / maxTime) * 100)
- });
- });
- fromPractice.practiceList = tempList || [];
- } catch (e: any) {}
- };
-
- const gotoMyWork = (pageTag = 'my_work') => {
- postMessage({
- api: 'open_app_page',
- content: {
- action: 'app',
- pageTag: pageTag,
- url: ''
- }
- });
- };
- onMounted(() => {
- window.addEventListener('resize', tabResize);
- listenerMessage('webViewOnResume', () => {
- tabResize();
- });
- onEvaluation();
- getPractice();
- });
- onUnmounted(() => {
- window.removeEventListener('resize', tabResize);
- });
- return () => (
- <div class={styles.exerciseContainer}>
- <MSticky
- position="top"
- onBarHeight={(height: number) => {
- topWrapHeight.value = height;
- }}>
- <MHeader border={false} background={'transparent'}>
- {{
- content: () => (
- <div class={styles.woringHeader}>
- <i
- onClick={() => {
- if (browser().isApp) {
- postMessage({
- api: 'back'
- });
- } else {
- router.back();
- }
- }}
- class={[
- 'van-badge__wrapper van-icon van-icon-arrow-left van-nav-bar__arrow',
- styles.leftArrow
- ]}></i>
- <Tabs
- ref={tabsRef}
- class={styles.tabSection}
- v-model:active={state.tabActive}
- shrink>
- <Tab name="EVALUATION" title="评测记录"></Tab>
- <Tab name="PRACTICE" title="练习记录"></Tab>
- </Tabs>
- </div>
- )
- }}
- </MHeader>
- </MSticky>
- <Tabs v-model:active={state.tabActive} class={styles.hideTabsHeader}>
- <Tab name="EVALUATION" title="评测记录">
- <div
- style={{
- overflow: 'hidden',
- height: `calc(100vh - ${topWrapHeight.value}px)`,
- display: 'flex',
- flexDirection: 'column'
- }}>
- <div class={[styles.cardSection, styles.EVALUATION_CARD]}>
- <img src={iconBird} class={styles.iconBird} />
- <div class={styles.scBox}>
- <img src={iconLl} class={styles.l1} />
- <img src={iconLc} class={styles.l2} />
- <img src={iconLr} class={styles.l3} />
- </div>
- <div class={styles.sCountSection}>
- <div class={styles.item}>
- <img src={iconNumber} class={styles.iconNumber} />
- <span class={styles.label}>评测次数</span>
- <span class={styles.value}>
- {infoDetail.value.evaluationNum}
- <i>次</i>
- </span>
- </div>
- <span class={styles.line}></span>
- <div class={styles.item} onClick={() => gotoMyWork()}>
- <img src={iconAlbum} class={styles.iconNumber} />
- <span class={styles.label}>作品数量</span>
- <span class={styles.value}>
- {infoDetail.value.userMusicNum}
- <i>首</i>
- </span>
- <img src={iconArrow} class={styles.iconArrow} />
- </div>
- </div>
- </div>
- <div class={styles.searchGroup}>
- <div class={[styles.section, styles.sectionSearch]}>
- <MSearch
- shape="round"
- inputBackground="white"
- background="transparent"
- placeholder="请输入曲目名称"
- onSearch={(val: any) => {
- formEvaluation.musicSheetName = val;
- refreshing.value = true;
- loading.value = true;
- onEvaluation();
- }}>
- {{
- left: () => (
- <div
- class={[
- styles.searchDropDown,
- state.showPopoverTime && styles.active
- ]}
- onClick={() => {
- state.showPopoverTime = true;
- }}>
- <span>筛选</span>
- <img
- class={styles.iconArrow}
- src={
- state.showPopoverTime
- ? iconArrowActive
- : iconArrow
- }
- />
- </div>
- )
- }}
- </MSearch>
- </div>
- </div>
- <div class={styles.listSection} style={{ flex: '1' }}>
- {showContact.value ? (
- <OFullRefresh
- v-model:modelValue={refreshing.value}
- onRefresh={onRefresh}
- style={
- {
- // minHeight: `calc(100vh - ${topWrapHeight.value}px)`
- }
- }>
- <List
- loading-text=" "
- loading={loading.value}
- finished={finished.value}
- finished-text=" "
- onLoad={getList}>
- {list.value.map((item: any) => (
- <DetailItem item={item} />
- ))}
- </List>
- </OFullRefresh>
- ) : (
- <MEmpty description="暂无内容" />
- )}
- </div>
- </div>
- </Tab>
- <Tab name="PRACTICE" title="练习记录">
- <div
- style={{
- overflow: 'hidden',
- height: `calc(100vh - ${topWrapHeight.value}px)`,
- display: 'flex',
- flexDirection: 'column'
- }}>
- <div class={[styles.cardSection, styles.EVALUATION_CARD]}>
- <img src={iconPP} class={styles.iconBirdPP} />
- <div class={styles.scBox}>
- <img src={iconPl} class={styles.l1} />
- <img src={iconPc} class={styles.l2} />
- <img src={iconPr} class={styles.l3} />
- </div>
- <div class={styles.sCountSection}>
- <div class={styles.item}>
- <img src={icon1} class={styles.iconNumber} />
- <span class={styles.label}>练习天数</span>
- <span class={styles.value}>
- {fromPractice.practiceDetail.practiceDays || 0}
- <i>天</i>
- </span>
- </div>
- <span class={styles.line}></span>
- <div class={styles.item} onClick={() => gotoMyWork()}>
- <img src={icon2} class={styles.iconNumber} />
- <span class={styles.label}>练习时长</span>
- <span class={styles.value}>
- {fromPractice.practiceDetail.practiceTimes
- ? Math.floor(
- fromPractice.practiceDetail.practiceTimes / 60
- )
- : 0}
- <i>分钟</i>
- </span>
- </div>
- </div>
- </div>
- <div class={styles.searchGroup}>
- <div class={[styles.section, styles.sectionSearch]}>
- <div
- class={[
- styles.practiceName,
- fromPractice.showTime && styles.active
- ]}
- onClick={() => {
- fromPractice.showTime = true;
- }}>
- {fromPractice.practiceMonthName}
- <img
- class={styles.iconArrow}
- src={fromPractice.showTime ? iconArrowActive : iconArrow}
- />
- </div>
- </div>
- </div>
- <div class={styles.listParent} style={{ flex: '1' }}>
- <div class={styles.listChild}>
- {fromPractice.showData ? (
- <div class={styles.practiceList}>
- {fromPractice.practiceList?.map((item: any) => (
- <div class={styles.practiceItem}>
- <span class={styles.time}>{item.date}</span>
- <div class={styles.lineBox}>
- <div class={styles.boxSection}>
- <div
- class={styles.box}
- style={{ width: item.rate + '%' }}></div>
- <p
- class={styles.long}
- style={{ left: item.rate + '%' }}>
- <span>{item.time}</span>
- 分钟
- </p>
- </div>
- </div>
- </div>
- ))}
- </div>
- ) : (
- <MEmpty description="暂无内容" />
- )}
- </div>
- </div>
- </div>
- </Tab>
- </Tabs>
- <TheTimeRange
- v-model:show={state.showPopoverTime}
- onConfirm={(val: any) => {
- formEvaluation.startTime = val.startTime
- ? val.startTime + ' 00:00:00'
- : '';
- formEvaluation.endTime = val.endTime
- ? val.endTime + ' 23:59:59'
- : '';
- state.showPopoverTime = false;
- refreshing.value = true;
- loading.value = true;
- onEvaluation();
- }}
- />
- <Popup
- v-model:show={fromPractice.showTime}
- position="bottom"
- round
- class={'popupBottomSearch'}>
- <DatePicker
- onCancel={() => {
- fromPractice.showTime = false;
- }}
- onConfirm={(val: any) => {
- fromPractice.showTime = false;
- getPractice();
- }}
- v-model={fromPractice.currentDate}
- formatter={formatterDatePicker}
- columnsType={['year', 'month']}
- />
- </Popup>
- </div>
- );
- }
- });
|