|
@@ -0,0 +1,194 @@
|
|
|
+import { Button, Cell, Empty, Image, Tab, Tabs } from 'vant'
|
|
|
+import { computed, defineComponent, nextTick, onMounted, reactive, ref } from 'vue'
|
|
|
+import styles from './index.module.less'
|
|
|
+import IconTrophy from './image/icon-trophy.png'
|
|
|
+import IconEmtry from './image/icon-emtry.png'
|
|
|
+import IconAvator from '@/common/images/icon_teacher.png'
|
|
|
+import request from '@/helpers/request'
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
+import { state as userInfo } from '@/state'
|
|
|
+
|
|
|
+interface IMusicItem {
|
|
|
+ loaded: boolean
|
|
|
+ musicSheetName: string
|
|
|
+ musicSubject: string
|
|
|
+ musicSheetId: number
|
|
|
+ evaluationId: number
|
|
|
+ rankingList: any
|
|
|
+ [_: string]: any
|
|
|
+}
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'leaderboard',
|
|
|
+ setup() {
|
|
|
+ const route = useRoute()
|
|
|
+ const router = useRouter()
|
|
|
+ const state = reactive({
|
|
|
+ tabIndex: 0,
|
|
|
+ musicList: [] as IMusicItem[]
|
|
|
+ })
|
|
|
+ const getMusicList = async () => {
|
|
|
+ try {
|
|
|
+ const {
|
|
|
+ data: { activityMusicVoList, shareUrl,subjectUrl }
|
|
|
+ } = await request.post(
|
|
|
+ `/api-student/open/activity/info/${route.query.id}`
|
|
|
+ )
|
|
|
+ if (Array.isArray(activityMusicVoList)) {
|
|
|
+ state.musicList = activityMusicVoList.map(n => {
|
|
|
+ n.rankingList = []
|
|
|
+ n.loaded = false
|
|
|
+ return n
|
|
|
+ })
|
|
|
+ }
|
|
|
+ img.value = subjectUrl
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ const getData = async () => {
|
|
|
+ if (state.musicList[state.tabIndex].loaded) return
|
|
|
+ try {
|
|
|
+ const { data } = await request.get(
|
|
|
+ '/api-student/open/activityEvaluationRecord/queryRankingList',
|
|
|
+ {
|
|
|
+ params: {
|
|
|
+ activityPlanId: route.query.id,
|
|
|
+ activityEvaluationId:
|
|
|
+ state.musicList[state.tabIndex].evaluationId,
|
|
|
+ limit: 10
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ if (Array.isArray(data.rankingList)) {
|
|
|
+ state.musicList[state.tabIndex].rankingList = data.rankingList
|
|
|
+ state.musicList[state.tabIndex].loaded = true
|
|
|
+ }
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ const img = ref()
|
|
|
+ const imgShow = ref(false)
|
|
|
+ const imgHeight = ref(100)
|
|
|
+
|
|
|
+ const openActive = () => {
|
|
|
+ router.back()
|
|
|
+ // router.replace({
|
|
|
+ // path: '/track-review-activity',
|
|
|
+ // query: {
|
|
|
+ // id: route.query.id
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(async () => {
|
|
|
+ await getMusicList()
|
|
|
+ await getData()
|
|
|
+ })
|
|
|
+ const user = computed(() => {
|
|
|
+ if (!state.musicList[state.tabIndex]) return {} as any
|
|
|
+ const userdata = userInfo.user.data
|
|
|
+ if (!userdata.userId) return {} as any
|
|
|
+ const rank = state.musicList[state.tabIndex]
|
|
|
+ const item = rank?.rankingList?.find(n => n.userId == userdata.userId)
|
|
|
+ let step = rank?.rankingList?.findIndex(n => n.userId == userdata.userId)
|
|
|
+ step = step > -1 ? step + 1 : 0
|
|
|
+ return {
|
|
|
+ join: rank.join,
|
|
|
+ score: rank.score,
|
|
|
+ isTop: item ? true : false,
|
|
|
+ heardUrl: userdata.heardUrl,
|
|
|
+ username: userdata.username,
|
|
|
+ userId: userdata.userId,
|
|
|
+ step
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const imgRef = ref()
|
|
|
+ return () => (
|
|
|
+ <div class={styles.leaderboard}>
|
|
|
+ <div class={styles.container}>
|
|
|
+ <div class={styles.headImg} ref={imgRef}>
|
|
|
+ <Image
|
|
|
+ width="100%"
|
|
|
+ fit="cover"
|
|
|
+ src={img.value}
|
|
|
+ onLoad={img => {
|
|
|
+ nextTick(() => {
|
|
|
+ imgShow.value = true
|
|
|
+ imgHeight.value = imgRef.value?.offsetHeight || 100
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ onError={(err) => {
|
|
|
+ console.log(err)
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ {imgShow.value && (
|
|
|
+ <Tabs
|
|
|
+ v-model:active={state.tabIndex}
|
|
|
+ class={styles.tabs}
|
|
|
+ animated
|
|
|
+ swipeable
|
|
|
+ titleInactiveColor="#fff"
|
|
|
+ titleActiveColor="rgba(224,146,144,1)"
|
|
|
+ onChange={index => getData()}
|
|
|
+ >
|
|
|
+ {state.musicList.map((item: IMusicItem) => {
|
|
|
+ return (
|
|
|
+ <Tab title={item.musicSheetName}>
|
|
|
+ <div
|
|
|
+ class={styles.tabContent}
|
|
|
+ style={{ height: `calc(100vh - ${imgHeight.value}px)` }}
|
|
|
+ >
|
|
|
+ <div class={styles.itemContent}>
|
|
|
+ <div class={styles.item}>
|
|
|
+ <div class={styles.left}>排名</div>
|
|
|
+ <div class={styles.center}>昵称</div>
|
|
|
+ <div class={styles.right}>评分</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {item.rankingList.map((n: any, index: number) => {
|
|
|
+ const t = (index + 1).toString().padStart(2, '0')
|
|
|
+ return (
|
|
|
+ <div class={styles.item}>
|
|
|
+ <div class={styles.left}>
|
|
|
+ {index == 0 ? <Image src={IconTrophy} /> : t}
|
|
|
+ </div>
|
|
|
+ <div class={styles.center}>
|
|
|
+ <Image
|
|
|
+ width="34px"
|
|
|
+ height="34px"
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={n.userAvatar || IconAvator}
|
|
|
+ />
|
|
|
+ <div class={styles.user}>
|
|
|
+ <div class={styles.name}>{n.username}</div>
|
|
|
+ <div class={styles.tag}>
|
|
|
+ <span>{n.userSubject}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.right}>
|
|
|
+ <div class={styles.fraction}>{n.score}分</div>
|
|
|
+ <div class={styles.time}>{n.joinDate}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+
|
|
|
+ {!item.rankingList.length && (
|
|
|
+ <Empty
|
|
|
+ image={IconEmtry}
|
|
|
+ description="该曲目暂无排名喔~"
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Tab>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </Tabs>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|