exercis-detail.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import OHeader from '@/components/m-header';
  2. import OSticky from '@/components/m-sticky';
  3. import OEmpty from '@/components/m-empty';
  4. import dayjs from 'dayjs';
  5. import { DatePicker, Popup, List, Image, CellGroup, Cell } from 'vant';
  6. import OFullRefresh from '@/components/m-full-refresh';
  7. import DetailItem from './modals/detail-item';
  8. import { defineComponent, onMounted, reactive, ref, nextTick } from 'vue';
  9. import { useRoute } from 'vue-router';
  10. import styles from './exercis-detail.module.less';
  11. import request from '@/helpers/request';
  12. import iconStudent from '@common/images/icon-student.png';
  13. import iconData from './images/icon-data.png';
  14. import { useRect } from '@vant/use';
  15. import { formatterDatePicker } from '@/helpers/utils';
  16. import { useEventListener, useWindowScroll } from '@vueuse/core';
  17. import { state as baseState } from '@/state';
  18. export default defineComponent({
  19. name: 'exercis-detail',
  20. setup() {
  21. const route = useRoute();
  22. const state = reactive({
  23. showPopoverTime: false,
  24. currentDate: [dayjs().format('YYYY'), dayjs().format('MM')],
  25. isClick: false,
  26. background: 'transparent',
  27. color: '#fff',
  28. practiceMonthName: route.query.practiceMonthName
  29. ? route.query.practiceMonthName
  30. : dayjs().format('YYYY') + '年' + dayjs().format('MM') + '月'
  31. });
  32. const forms = reactive({
  33. practiceMonth: route.query.practiceMonth
  34. ? route.query.practiceMonth
  35. : state.currentDate[0] + '' + state.currentDate[1],
  36. page: 1,
  37. rows: 20
  38. });
  39. const refreshing = ref(false);
  40. const loading = ref(false);
  41. const finished = ref(false);
  42. const showContact = ref(false);
  43. const infoDetail = ref({} as any);
  44. const list = ref([]);
  45. const getList = async () => {
  46. if (state.isClick) {
  47. return;
  48. }
  49. state.isClick = true;
  50. if (refreshing.value) {
  51. list.value = [];
  52. forms.page = 1;
  53. refreshing.value = false;
  54. }
  55. try {
  56. const res = await request.post(`/edu-app/musicPracticeRecord/page`, {
  57. data: { ...forms, feature: 'EVALUATION' }
  58. });
  59. if (list.value.length > 0 && res.data.current === 1) {
  60. return;
  61. }
  62. list.value = list.value.concat(res.data.rows || []);
  63. forms.page = res.data.current + 1;
  64. showContact.value = list.value.length > 0;
  65. loading.value = false;
  66. finished.value = res.data.current >= res.data.pages;
  67. } catch {
  68. showContact.value = false;
  69. finished.value = true;
  70. }
  71. state.isClick = false;
  72. };
  73. const getDetail = async () => {
  74. try {
  75. const res = await request.get(`/edu-app/student/detail`, {
  76. params: {
  77. id: baseState.user.data?.id
  78. }
  79. });
  80. infoDetail.value = { ...res.data };
  81. } catch (e: any) {}
  82. };
  83. const topWrap = ref();
  84. const topWrapHeight = ref(0);
  85. onMounted(async () => {
  86. useEventListener(document, 'scroll', () => {
  87. const { y } = useWindowScroll();
  88. if (y.value > 52) {
  89. state.background = '#fff';
  90. state.color = '#323333';
  91. } else {
  92. state.background = 'transparent';
  93. state.color = '#fff';
  94. }
  95. });
  96. await getList();
  97. await getDetail();
  98. nextTick(() => {
  99. const { height } = useRect(topWrap.value);
  100. topWrapHeight.value = height;
  101. });
  102. });
  103. const checkTimer = (val: any) => {
  104. forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1];
  105. state.practiceMonthName =
  106. val.selectedValues[0] + '年' + val.selectedValues[1] + '月';
  107. state.showPopoverTime = false;
  108. refreshing.value = true;
  109. getList();
  110. };
  111. const onRefresh = () => {
  112. finished.value = false;
  113. // 重新加载数据
  114. // 将 loading 设置为 true,表示处于加载状态
  115. loading.value = true;
  116. getList();
  117. };
  118. return () => (
  119. <>
  120. <div class={[styles.exercisContainer]}>
  121. <div class={styles.topWrap} ref={topWrap}>
  122. <OSticky position="top">
  123. <OHeader
  124. border={false}
  125. background={state.background}
  126. color={state.color}
  127. />
  128. </OSticky>
  129. <div class={styles.topInfo}>
  130. <div class={styles.topInfoLeft}>
  131. <div class={styles.headWrap}>
  132. <Image
  133. src={
  134. infoDetail.value.avatar
  135. ? infoDetail.value.avatar
  136. : iconStudent
  137. }
  138. fit="cover"
  139. width="68px"
  140. height="68px"
  141. />
  142. </div>
  143. <div class={styles.infoMsg}>
  144. <p>{infoDetail.value.nickname}</p>
  145. <div class={styles.tag}>
  146. {infoDetail.value.subjectNames
  147. ? infoDetail.value.subjectNames
  148. : '暂无声部'}
  149. </div>
  150. </div>
  151. </div>
  152. <div class={styles.topInfoRight}>
  153. <div class={styles.infoDay}>
  154. <p class={styles.infoDayMain}>
  155. {infoDetail.value.practiceDays
  156. ? infoDetail.value.practiceDays
  157. : 0}
  158. <span>天</span>
  159. </p>
  160. <p class={styles.infoDaysub}>练习天数</p>
  161. </div>
  162. <div class={styles.infoTime}>
  163. <p class={styles.infoDayMain}>
  164. {infoDetail.value.practiceTimes
  165. ? infoDetail.value.practiceTimes
  166. : 0}
  167. <span>分钟</span>
  168. </p>
  169. <p class={styles.infoDaysub}>练习时长</p>
  170. </div>
  171. </div>
  172. </div>
  173. <CellGroup inset>
  174. <Cell
  175. class={styles.select}
  176. center
  177. isLink
  178. onClick={() => (state.showPopoverTime = true)}>
  179. {{
  180. icon: () => <img class={styles.icon} src={iconData} />,
  181. title: () => (
  182. <div class="van-ellipsis">{state.practiceMonthName}</div>
  183. )
  184. }}
  185. </Cell>
  186. </CellGroup>
  187. </div>
  188. {showContact.value ? (
  189. <OFullRefresh
  190. v-model:modelValue={refreshing.value}
  191. onRefresh={onRefresh}
  192. style={{ minHeight: `calc(100vh - ${topWrapHeight.value}px)` }}>
  193. <List
  194. loading-text=" "
  195. finished={finished.value}
  196. finished-text=" "
  197. onLoad={getList}>
  198. {list.value.map((item: any) => (
  199. <DetailItem item={item} />
  200. ))}
  201. </List>
  202. </OFullRefresh>
  203. ) : (
  204. <OEmpty
  205. description="暂无练习统计"
  206. style={{ height: `calc(100vh - ${topWrapHeight.value}px)` }}
  207. />
  208. )}
  209. </div>
  210. <Popup
  211. v-model:show={state.showPopoverTime}
  212. position="bottom"
  213. round
  214. class={'popupBottomSearch'}>
  215. <DatePicker
  216. onCancel={() => {
  217. state.showPopoverTime = false;
  218. }}
  219. onConfirm={checkTimer}
  220. v-model={state.currentDate}
  221. formatter={formatterDatePicker}
  222. columnsType={['year', 'month']}
  223. />
  224. </Popup>
  225. </>
  226. );
  227. }
  228. });