|
@@ -0,0 +1,300 @@
|
|
|
+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'
|
|
|
+import { useRect } from '@vant/use'
|
|
|
+
|
|
|
+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[],
|
|
|
+ isSignup: false, // 是否报名
|
|
|
+ isChallenge: false // 是否挑战过
|
|
|
+ })
|
|
|
+ const getMusicList = async () => {
|
|
|
+ try {
|
|
|
+ const { data } = await request.post(
|
|
|
+ `/api-student/open/activity/info/${route.query.id}`
|
|
|
+ )
|
|
|
+ if (Array.isArray(data.activityMusicVoList)) {
|
|
|
+ state.musicList = data.activityMusicVoList.map(n => {
|
|
|
+ n.rankingList = []
|
|
|
+ return n
|
|
|
+ })
|
|
|
+ state.isChallenge = data.activityMusicVoList.filter(n => n.join)
|
|
|
+ .length
|
|
|
+ ? true
|
|
|
+ : false
|
|
|
+ }
|
|
|
+ img.value = data.subjectUrl
|
|
|
+ state.isSignup = data.join ? true : false
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ const getData = async () => {
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ } 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()
|
|
|
+ const userRef = 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(() => {
|
|
|
+ const { height } = useRect(imgRef)
|
|
|
+ imgShow.value = true
|
|
|
+ imgHeight.value = height || 100
|
|
|
+ })
|
|
|
+ }}
|
|
|
+ onError={err => {
|
|
|
+ console.log(err)
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ {imgShow.value && (
|
|
|
+ <Tabs
|
|
|
+ v-model:active={state.tabIndex}
|
|
|
+ class={styles.tabs}
|
|
|
+ animated
|
|
|
+ swipeable
|
|
|
+ titleInactiveColor="rgba(153,152,155,1)"
|
|
|
+ titleActiveColor="#fff"
|
|
|
+ onChange={index => getData()}
|
|
|
+ >
|
|
|
+ {state.musicList.map((item: IMusicItem) => {
|
|
|
+ return (
|
|
|
+ <Tab title={item.musicSheetName}>
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.tabContent,
|
|
|
+ !state.isSignup || !state.isChallenge || user.value.join
|
|
|
+ ? styles.hasUser
|
|
|
+ : null
|
|
|
+ ]}
|
|
|
+ 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="38px"
|
|
|
+ height="38px"
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={n.userAvatar || IconAvator}
|
|
|
+ />
|
|
|
+ <div class={styles.user}>
|
|
|
+ <div class={styles.userContent}>
|
|
|
+ <span class={styles.name}>
|
|
|
+ {n.username}
|
|
|
+ </span>
|
|
|
+ <span class={styles.tag}>
|
|
|
+ {n.userSubject}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class={styles.times}>{n.joinDate}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.right}>
|
|
|
+ <div class={styles.fraction}>{n.score}分</div>
|
|
|
+ <div class={styles.time}>
|
|
|
+ 第 {n.times} 次评测
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+
|
|
|
+ {!item.rankingList.length && (
|
|
|
+ <Empty
|
|
|
+ image={IconEmtry}
|
|
|
+ description="该曲目暂无排名喔~"
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div class="van-safe-area-bottom"></div>
|
|
|
+ </div>
|
|
|
+ </Tab>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </Tabs>
|
|
|
+ )}
|
|
|
+ {!state.isSignup ? (
|
|
|
+ <div
|
|
|
+ ref={userRef}
|
|
|
+ class={[styles.activeUser, 'van-safe-area-bottom']}
|
|
|
+ >
|
|
|
+ <Cell
|
|
|
+ center
|
|
|
+ title={user.value.username}
|
|
|
+ label="您尚未报名参赛"
|
|
|
+ v-slots={{
|
|
|
+ icon: () => (
|
|
|
+ <Image
|
|
|
+ class={styles.avator}
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={user.value.heardUrl || IconAvator}
|
|
|
+ />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ ) : !state.isChallenge ? (
|
|
|
+ <div
|
|
|
+ ref={userRef}
|
|
|
+ class={[styles.activeUser, 'van-safe-area-bottom']}
|
|
|
+ >
|
|
|
+ <Cell
|
|
|
+ center
|
|
|
+ title={user.value.username}
|
|
|
+ label="您尚未评测哦!"
|
|
|
+ v-slots={{
|
|
|
+ icon: () => (
|
|
|
+ <Image
|
|
|
+ class={styles.avator}
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={user.value.heardUrl || IconAvator}
|
|
|
+ />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ ) : user.value.join ? (
|
|
|
+ <div
|
|
|
+ ref={userRef}
|
|
|
+ class={[styles.activeUser, 'van-safe-area-bottom']}
|
|
|
+ >
|
|
|
+ <Cell
|
|
|
+ center
|
|
|
+ title={user.value.username}
|
|
|
+ v-slots={{
|
|
|
+ icon: () => (
|
|
|
+ <Image
|
|
|
+ class={styles.avator}
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={user.value.heardUrl || IconAvator}
|
|
|
+ />
|
|
|
+ ),
|
|
|
+ label: () => {
|
|
|
+ if (user.value.isTop) {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ 您的评测已上榜! 当前排名
|
|
|
+ <span style={{ color: '#FA6400' }}>
|
|
|
+ {' '}
|
|
|
+ {user.value.step}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ return <div>您的评测暂未上榜,快去挑战吧!</div>
|
|
|
+ }
|
|
|
+ },
|
|
|
+ value: () => (
|
|
|
+ <span class={styles.num}>{user.value.score}分</span>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ ) : null}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|