exercis-detail.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import OHeader from '@/components/o-header'
  2. import OSticky from '@/components/o-sticky'
  3. import OEmpty from '@/components/o-empty'
  4. import dayjs from 'dayjs'
  5. import {
  6. Icon,
  7. Popover,
  8. DatePicker,
  9. DatePickerColumnType,
  10. Popup,
  11. List,
  12. PullRefresh,
  13. showToast,
  14. Dialog,
  15. Image,
  16. NoticeBar
  17. } from 'vant'
  18. import OFullRefresh from '@/components/o-full-refresh'
  19. import DetailItem from './modals/detail-item'
  20. import { defineComponent, onMounted, reactive, ref, onDeactivated, nextTick } from 'vue'
  21. import { useRoute, useRouter } from 'vue-router'
  22. import styles from './exercis-detail.module.less'
  23. import request from '@/helpers/request'
  24. import questIcon from '@/school/images/quest-icon.png'
  25. import defaultIcon from '@/school/images/default-icon.png'
  26. import iconStudent from '@common/images/icon_student.png'
  27. import { state as globalState } from '@/state'
  28. import { useRect } from '@vant/use'
  29. import { formatterDatePicker } from '@/helpers/utils'
  30. export default defineComponent({
  31. name: 'exercis-detail',
  32. setup() {
  33. const router = useRouter()
  34. const route = useRoute()
  35. const platformApi = ref(globalState.platformApi)
  36. const state = reactive({
  37. showPopoverTime: false,
  38. showPopoverOrchestra: false,
  39. currentDate: [dayjs().format('YYYY'), dayjs().format('MM')],
  40. actions: [
  41. { text: '全部乐团', color: 'var(--van-primary-color)' },
  42. { text: '交付团' },
  43. { text: '晋升团' }
  44. ],
  45. id: route.query.id,
  46. heightV: 0 as number,
  47. scrollTop: 0 as number,
  48. isClick: false
  49. })
  50. const forms = reactive({
  51. practiceMonth: route.query.practiceMonth
  52. ? route.query.practiceMonth
  53. : state.currentDate[0] + '' + state.currentDate[1],
  54. practiceMonthName: route.query.practiceMonthName
  55. ? route.query.practiceMonthName
  56. : state.currentDate[0] + '年' + state.currentDate[1] + '月',
  57. orchestraId: '',
  58. orchestraName: '',
  59. page: 1,
  60. rows: 20,
  61. userId: route.query.id,
  62. clientType: route.query.clientType ? route.query.clientType : globalState.platformType
  63. })
  64. const showTip = ref(false)
  65. const minDate = ref(new Date(dayjs().subtract(10, 'year').format('YYYY-MM-DD')))
  66. const maxDate = ref(new Date(dayjs().add(10, 'year').format('YYYY-MM-DD')))
  67. const columnsType = ref<DatePickerColumnType[]>(['year', 'month'])
  68. const refreshing = ref(false)
  69. const loading = ref(false)
  70. const finished = ref(false)
  71. const showContact = ref(false)
  72. const infoDetail = ref({} as any)
  73. const list = ref([])
  74. const getList = async () => {
  75. if (state.isClick) {
  76. return
  77. }
  78. state.isClick = true
  79. if (refreshing.value) {
  80. list.value = []
  81. forms.page = 1
  82. refreshing.value = false
  83. }
  84. try {
  85. const res = await request.post(`${platformApi.value}/musicPracticeRecord/page`, {
  86. data: { ...forms, feature: 'EVALUATION' }
  87. })
  88. if (list.value.length > 0 && res.data.current === 1) {
  89. return
  90. }
  91. list.value = list.value.concat(res.data.rows || [])
  92. forms.page = res.data.current + 1
  93. showContact.value = list.value.length > 0
  94. loading.value = false
  95. console.log(res.data, res.data.current >= res.data.pages)
  96. finished.value = res.data.current >= res.data.pages
  97. } catch (e: any) {
  98. // console.log(e, 'e')
  99. const message = e.message
  100. // showToast(message)
  101. showContact.value = false
  102. finished.value = true
  103. }
  104. state.isClick = false
  105. }
  106. const getDetail = async () => {
  107. if(forms.clientType === 'TEACHER'){
  108. try {
  109. const res = await request.post(`${platformApi.value}/teacher/detail`, {
  110. data:{
  111. teacherId: state.id
  112. }
  113. })
  114. infoDetail.value = { ...res.data }
  115. infoDetail.value.subjectNames = res.data.subjectName.split(',')
  116. } catch (e: any) {
  117. // console.log(e, 'e')
  118. const message = e.message
  119. // showToast(message)
  120. }
  121. } else {
  122. try {
  123. const res = await request.get(`/api-backend/student/detail/${state.id}`)
  124. console.log(res)
  125. infoDetail.value = { ...res.data }
  126. } catch (e: any) {
  127. // console.log(e, 'e')
  128. const message = e.message
  129. // showToast(message)
  130. }
  131. }
  132. }
  133. const topWrap = ref()
  134. const topWrapHeight = ref(0)
  135. onMounted(async () => {
  136. await getList()
  137. await getDetail()
  138. window.addEventListener('scroll', handleScroll)
  139. nextTick(() => {
  140. const { height } = useRect(topWrap.value)
  141. topWrapHeight.value = height
  142. })
  143. })
  144. onDeactivated(() => {
  145. window.removeEventListener('scroll', handleScroll)
  146. })
  147. const handleScroll = () => {
  148. const scrollTop =
  149. window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
  150. state.scrollTop = scrollTop
  151. }
  152. const getHeight = (dataHeight: number) => {
  153. state.heightV = dataHeight
  154. console.log(dataHeight, 'dataHeight')
  155. }
  156. const checkTimer = (val: any) => {
  157. forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1]
  158. forms.practiceMonthName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
  159. state.showPopoverTime = false
  160. refreshing.value = true
  161. getList()
  162. }
  163. const onRefresh = () => {
  164. finished.value = false
  165. // 重新加载数据
  166. // 将 loading 设置为 true,表示处于加载状态
  167. loading.value = true
  168. getList()
  169. }
  170. const onBack = () => {
  171. router.go(-1)
  172. }
  173. return () => (
  174. <>
  175. <div class={[styles.exercisContainer, !showContact.value && 'emptyRootContainer']}>
  176. <div class={styles.topWrap} ref={topWrap}>
  177. <OSticky position="top" background="#F8F8F8" onGetHeight={getHeight}>
  178. <OHeader
  179. border={false}
  180. background={state.heightV > state.scrollTop ? 'transparent' : '#fff'}
  181. >
  182. {{
  183. right: () => (
  184. <Icon
  185. name={questIcon}
  186. size={22}
  187. color="#333"
  188. onClick={() => {
  189. showTip.value = true
  190. }}
  191. />
  192. )
  193. }}
  194. </OHeader>
  195. </OSticky>
  196. <div class={styles.topInfo}>
  197. <div class={styles.topInfoLeft}>
  198. <div class={styles.headWrap}>
  199. <Image
  200. src={infoDetail.value.avatar ? infoDetail.value.avatar : iconStudent}
  201. fit="cover"
  202. width="68px"
  203. height="68px"
  204. />
  205. </div>
  206. <div class={styles.infoMsg}>
  207. <p>{infoDetail.value.nickname}</p>
  208. {forms.clientType === 'TEACHER' ? <NoticeBar class={styles.teacherSubject}>
  209. {infoDetail.value?.subjectNames?.map((item: any) => {
  210. return <div class={styles.tag}>{item}</div>
  211. })}
  212. </NoticeBar> : (
  213. <div class={styles.tag}>
  214. {infoDetail.value.subjectNames ? infoDetail.value.subjectNames : '暂无声部'}
  215. </div>
  216. )}
  217. </div>
  218. </div>
  219. <div class={styles.topInfoRight}>
  220. <div class={styles.infoDay}>
  221. <p class={styles.infoDayMain}>
  222. {infoDetail.value.practiceDays ? infoDetail.value.practiceDays : 0}
  223. {''}
  224. <span>天</span>
  225. </p>
  226. <p class={styles.infoDaysub}>练习天数</p>
  227. </div>
  228. <div class={styles.infoTime}>
  229. <p class={styles.infoDayMain}>
  230. {infoDetail.value.practiceTimes ? infoDetail.value.practiceTimes : 0}
  231. {''}
  232. <span>分钟</span>
  233. </p>
  234. <p class={styles.infoDaysub}>练习时长</p>
  235. </div>
  236. </div>
  237. </div>
  238. <div class={'searchGroup-single'} style="padding-top: 0 !important;">
  239. <div
  240. class={['searchItem', state.showPopoverTime ? 'searchItem-active' : '']}
  241. onClick={() => {
  242. state.showPopoverTime = true
  243. }}
  244. >
  245. <span>{forms.practiceMonthName}</span>
  246. </div>
  247. </div>
  248. {/* <div class={styles.chioseWrap}> */}
  249. {/* <div style={{ padding: '12px 13px', background: 'transparent' }}>
  250. <div
  251. class={styles.searchBand}
  252. onClick={() => {
  253. state.showPopoverTime = true
  254. }}
  255. >
  256. {forms.practiceMonthName}
  257. <Icon name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'} />
  258. </div>
  259. </div> */}
  260. {/* <div style={{ padding: '12px 13px', background: 'transparent' }}>
  261. <Popover
  262. v-model:show={state.showPopoverOrchestra}
  263. actions={state.actions}
  264. showArrow={false}
  265. placement="bottom-start"
  266. offset={[0, 12]}
  267. >
  268. {{
  269. reference: () => (
  270. <div class={styles.searchBand}>
  271. 全部乐团
  272. <Icon name={state.showPopoverOrchestra ? 'arrow-up' : 'arrow-down'} />
  273. </div>
  274. )
  275. }}
  276. </Popover>
  277. </div> */}
  278. {/* </div> */}
  279. </div>
  280. {showContact.value ? (
  281. <OFullRefresh
  282. v-model:modelValue={refreshing.value}
  283. onRefresh={onRefresh}
  284. style="min-height: 100vh;"
  285. >
  286. <List
  287. loading-text=" "
  288. // v-model:loading={loading.value}
  289. finished={finished.value}
  290. finished-text="没有更多了"
  291. onLoad={getList}
  292. >
  293. {list.value.map((item: any) => (
  294. <DetailItem item={item} />
  295. ))}
  296. </List>
  297. </OFullRefresh>
  298. ) : (
  299. <OEmpty tips="暂无练习统计" />
  300. )}
  301. </div>
  302. <Popup
  303. v-model:show={state.showPopoverTime}
  304. position="bottom"
  305. round
  306. class={'popupBottomSearch'}
  307. >
  308. <DatePicker
  309. onCancel={() => {
  310. state.showPopoverTime = false
  311. }}
  312. onConfirm={checkTimer}
  313. v-model={state.currentDate}
  314. minDate={minDate.value}
  315. maxDate={maxDate.value}
  316. formatter={formatterDatePicker}
  317. columnsType={columnsType.value}
  318. />
  319. </Popup>
  320. <Dialog
  321. class="exercisDetailDialog"
  322. v-model:show={showTip.value}
  323. title="提示框"
  324. confirmButtonText="我知道了"
  325. v-slots={{
  326. title: () => (
  327. <div class={styles.DialogTitle}>
  328. <span></span>
  329. <p>什么是练习数据</p>
  330. </div>
  331. ),
  332. default: () => (
  333. <div class={styles.DialogConent}>
  334. <p>
  335. 练习数据是{forms.clientType == 'TEACHER' ? '' : '学员'}通过云教练自主练习的数据统计,可根据时间段查询{forms.clientType == 'TEACHER' ? '' : '学员'}的练习天数和练习时长{' '}
  336. </p>
  337. <p>练习天数:当天有曲目播放或测评记录即算练习</p>
  338. <p>练习时长:曲目播放和曲目测评的时长总和</p>
  339. </div>
  340. )
  341. }}
  342. ></Dialog>
  343. </>
  344. )
  345. }
  346. })