index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { Button, Cell, Empty, Image, Tab, Tabs } from 'vant'
  2. import {
  3. computed,
  4. defineComponent,
  5. nextTick,
  6. onMounted,
  7. reactive,
  8. ref
  9. } from 'vue'
  10. import styles from './index.module.less'
  11. import IconTrophy from './image/icon-trophy.png'
  12. import IconEmtry from './image/icon-emtry.png'
  13. import IconAvator from '@/common/images/icon_teacher.png'
  14. import request from '@/helpers/request'
  15. import { useRoute, useRouter } from 'vue-router'
  16. import { state as userInfo } from '@/state'
  17. import { useRect } from '@vant/use'
  18. interface IMusicItem {
  19. loaded: boolean
  20. musicSheetName: string
  21. musicSubject: string
  22. musicSheetId: number
  23. evaluationId: number
  24. rankingList: any
  25. [_: string]: any
  26. }
  27. export default defineComponent({
  28. name: 'leaderboard',
  29. setup() {
  30. const route = useRoute()
  31. const router = useRouter()
  32. const state = reactive({
  33. tabIndex: 0,
  34. musicList: [] as IMusicItem[],
  35. isSignup: false, // 是否报名
  36. isChallenge: false, // 是否挑战过
  37. score: 0
  38. })
  39. const getMusicList = async () => {
  40. try {
  41. const { data } = await request.post(
  42. `/api-student/open/activity/info/${route.query.id}`
  43. )
  44. if (Array.isArray(data.activityMusicVoList)) {
  45. state.musicList = data.activityMusicVoList.map(n => {
  46. n.rankingList = []
  47. return n
  48. })
  49. state.isChallenge = data.activityMusicVoList.filter(n => n.join)
  50. .length
  51. ? true
  52. : false
  53. }
  54. img.value = data.subjectUrl
  55. state.isSignup = data.join ? true : false
  56. } catch (error) {}
  57. }
  58. const getData = async () => {
  59. try {
  60. const { data } = await request.get(
  61. '/api-student/open/activityEvaluationRecord/queryRankingList',
  62. {
  63. params: {
  64. activityPlanId: route.query.id,
  65. activityEvaluationId:
  66. state.musicList[state.tabIndex].evaluationId,
  67. limit: 10
  68. }
  69. }
  70. )
  71. if (Array.isArray(data.rankingList)) {
  72. state.musicList[state.tabIndex].rankingList = data.rankingList
  73. }
  74. if (data.userActivityRankingVo){
  75. state.score = data.userActivityRankingVo.score
  76. }
  77. } catch (error) {}
  78. }
  79. const img = ref()
  80. const imgShow = ref(false)
  81. const imgHeight = ref(100)
  82. const openActive = () => {
  83. router.back()
  84. // router.replace({
  85. // path: '/track-review-activity',
  86. // query: {
  87. // id: route.query.id
  88. // }
  89. // })
  90. }
  91. onMounted(async () => {
  92. await getMusicList()
  93. await getData()
  94. })
  95. const user = computed(() => {
  96. if (!state.musicList[state.tabIndex]) return {} as any
  97. const userdata = userInfo.user.data
  98. if (!userdata.userId) return {} as any
  99. const rank = state.musicList[state.tabIndex]
  100. const item = rank?.rankingList?.find(n => n.userId == userdata.userId)
  101. let step = rank?.rankingList?.findIndex(n => n.userId == userdata.userId)
  102. step = step > -1 ? step + 1 : 0
  103. return {
  104. join: rank.join,
  105. score: item?.score || 0,
  106. isTop: item ? true : false,
  107. heardUrl: userdata.heardUrl,
  108. username: userdata.username,
  109. userId: userdata.userId,
  110. step
  111. }
  112. })
  113. const imgRef = ref()
  114. const userRef = ref()
  115. return () => (
  116. <div class={styles.leaderboard}>
  117. <div class={styles.container}>
  118. <div class={styles.headImg} ref={imgRef}>
  119. <Image
  120. width="100%"
  121. fit="cover"
  122. src={img.value}
  123. onLoad={img => {
  124. nextTick(() => {
  125. const { height } = useRect(imgRef)
  126. imgShow.value = true
  127. imgHeight.value = height || 100
  128. })
  129. }}
  130. onError={err => {
  131. console.log(err)
  132. }}
  133. />
  134. </div>
  135. {imgShow.value && (
  136. <Tabs
  137. v-model:active={state.tabIndex}
  138. class={styles.tabs}
  139. animated
  140. swipeable
  141. titleInactiveColor="rgba(153,152,155,1)"
  142. titleActiveColor="#fff"
  143. onChange={index => getData()}
  144. >
  145. {state.musicList.map((item: IMusicItem) => {
  146. return (
  147. <Tab title={item.musicSheetName}>
  148. <div
  149. class={[
  150. styles.tabContent,
  151. !state.isSignup || !state.isChallenge || user.value.join
  152. ? styles.hasUser
  153. : null
  154. ]}
  155. style={{ height: `calc(100vh - ${imgHeight.value}px)` }}
  156. >
  157. <div class={styles.itemContent}>
  158. <div class={styles.item}>
  159. <div class={styles.left}>排名</div>
  160. <div class={styles.center}>昵称</div>
  161. <div class={styles.right}>评分</div>
  162. </div>
  163. {item.rankingList.map((n: any, index: number) => {
  164. const t = (index + 1).toString().padStart(2, '0')
  165. return (
  166. <div class={styles.item}>
  167. <div class={styles.left}>
  168. {index == 0 ? <Image src={IconTrophy} /> : t}
  169. </div>
  170. <div class={styles.center}>
  171. <Image
  172. width="38px"
  173. height="38px"
  174. fit="cover"
  175. round
  176. src={n.userAvatar || IconAvator}
  177. />
  178. <div class={styles.user}>
  179. <div class={styles.userContent}>
  180. <span class={styles.name}>
  181. {n.username}
  182. </span>
  183. <span class={styles.tag}>
  184. {n.userSubject}
  185. </span>
  186. </div>
  187. <div class={styles.times}>{n.joinDate}</div>
  188. </div>
  189. </div>
  190. <div class={styles.right}>
  191. <div class={styles.fraction}>{n.score}分</div>
  192. <div class={styles.time}>
  193. 第 {n.times} 次评测
  194. </div>
  195. </div>
  196. </div>
  197. )
  198. })}
  199. {!item.rankingList.length && (
  200. <Empty
  201. image={IconEmtry}
  202. description="该曲目暂无排名喔~"
  203. />
  204. )}
  205. </div>
  206. <div class="van-safe-area-bottom"></div>
  207. </div>
  208. </Tab>
  209. )
  210. })}
  211. </Tabs>
  212. )}
  213. {!state.isSignup ? (
  214. <div
  215. ref={userRef}
  216. class={[styles.activeUser, 'van-safe-area-bottom']}
  217. >
  218. <Cell
  219. center
  220. title={user.value.username}
  221. label="您尚未报名参赛"
  222. v-slots={{
  223. icon: () => (
  224. <Image
  225. class={styles.avator}
  226. fit="cover"
  227. round
  228. src={user.value.heardUrl || IconAvator}
  229. />
  230. )
  231. }}
  232. />
  233. </div>
  234. ) : !state.isChallenge ? (
  235. <div
  236. ref={userRef}
  237. class={[styles.activeUser, 'van-safe-area-bottom']}
  238. >
  239. <Cell
  240. center
  241. title={user.value.username}
  242. label="您尚未评测哦!"
  243. v-slots={{
  244. icon: () => (
  245. <Image
  246. class={styles.avator}
  247. fit="cover"
  248. round
  249. src={user.value.heardUrl || IconAvator}
  250. />
  251. )
  252. }}
  253. />
  254. </div>
  255. ) : user.value.join ? (
  256. <div
  257. ref={userRef}
  258. class={[styles.activeUser, 'van-safe-area-bottom']}
  259. >
  260. <Cell
  261. center
  262. title={user.value.username}
  263. v-slots={{
  264. icon: () => (
  265. <Image
  266. class={styles.avator}
  267. fit="cover"
  268. round
  269. src={user.value.heardUrl || IconAvator}
  270. />
  271. ),
  272. label: () => {
  273. if (user.value.isTop) {
  274. return (
  275. <div>
  276. 您的评测已上榜! 当前排名
  277. <span style={{ color: '#FA6400' }}>
  278. {' '}
  279. {user.value.step}
  280. </span>
  281. </div>
  282. )
  283. } else {
  284. return <div>您的评测暂未上榜,快去挑战吧!</div>
  285. }
  286. },
  287. value: () => {
  288. if (!user.value.score && !state.score){
  289. return
  290. }
  291. return <span class={styles.num}>{user.value.score || state.score}分</span>
  292. }
  293. }}
  294. />
  295. </div>
  296. ) : null}
  297. </div>
  298. </div>
  299. )
  300. }
  301. })