|
@@ -0,0 +1,250 @@
|
|
|
+import { Button, Cell, Empty, Image, Tab, Tabs } from 'vant'
|
|
|
+import { computed, defineComponent, 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 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 }
|
|
|
+ } = 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 = shareUrl
|
|
|
+ } 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(42)
|
|
|
+
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return () => (
|
|
|
+ <div class={styles.leaderboard}>
|
|
|
+ <div class={styles.container}>
|
|
|
+ <div class={styles.headImg}>
|
|
|
+ <Image
|
|
|
+ width="100%"
|
|
|
+ fit="cover"
|
|
|
+ src={img.value}
|
|
|
+ onLoad={img => {
|
|
|
+ imgHeight.value = img.target.height
|
|
|
+ imgShow.value = true
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </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')
|
|
|
+ const time = (n.joinDate + '').split(' ')[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}
|
|
|
+ />
|
|
|
+ <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}>{time}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+
|
|
|
+ {!item.rankingList.length && (
|
|
|
+ <Empty
|
|
|
+ image={IconEmtry}
|
|
|
+ description="该曲目暂无排名喔~"
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Tab>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </Tabs>
|
|
|
+ )}
|
|
|
+ {user.value.userId && (
|
|
|
+ <div class={styles.activeUser}>
|
|
|
+ <Cell
|
|
|
+ center
|
|
|
+ title={user.value.username}
|
|
|
+ v-slots={{
|
|
|
+ icon: () => (
|
|
|
+ <Image
|
|
|
+ class={styles.avator}
|
|
|
+ fit="cover"
|
|
|
+ round
|
|
|
+ src={user.value.heardUrl}
|
|
|
+ />
|
|
|
+ ),
|
|
|
+ label: () => {
|
|
|
+ if (user.value.join) {
|
|
|
+ if (user.value.isTop) {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ 您的评测已上榜! 当前排名
|
|
|
+ <span style={{ color: '#FA6400' }}>
|
|
|
+ {' '}
|
|
|
+ {user.value.step}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ 您的评测暂未上榜,快去
|
|
|
+ <span style={{ color: '#FA6400' }}>挑战</span>
|
|
|
+ 吧!
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return <div>您尚未报名参赛</div>
|
|
|
+ }
|
|
|
+ },
|
|
|
+ value: () => {
|
|
|
+ if (user.value.join) {
|
|
|
+ return (
|
|
|
+ <span class={styles.num}>{user.value.score}分</span>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ return (
|
|
|
+ <Button
|
|
|
+ class={styles.btn}
|
|
|
+ round
|
|
|
+ size="small"
|
|
|
+ type="primary"
|
|
|
+ onClick={() => openActive()}
|
|
|
+ >
|
|
|
+ 立刻挑战
|
|
|
+ </Button>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|