瀏覽代碼

Squashed commit of the following:

commit a75c1faa265346a96b65a983cc7923da32dab68c
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 18:07:25 2022 +0800

    fix

commit aa0eb6c1b5bf8a4d0ffe0d8517d980ef1fbe8c8e
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 18:04:22 2022 +0800

    fix

commit 1425db77c90f97a95e182b1c8ef5dc03d2252841
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 18:01:01 2022 +0800

    fix

commit 24b483ceb728a8d27e0e17c2ebd898cd6bdcc078
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 17:59:59 2022 +0800

    fix

commit 80a9d2faeaf5a28208237e28142b1dd5f080ba33
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 17:46:53 2022 +0800

    fix

commit 852d8e9e74cd9319baccd5fd95c2678c2f942458
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 17:11:01 2022 +0800

    fix

commit 1b9684257ef59bbbf23c32282e563a828aa9b1cc
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 16:22:50 2022 +0800

    fix

commit 9ab41a9c39118a58209fdb0abf11279cebe580f5
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 16:16:50 2022 +0800

    老师查看排行榜

commit 11735b298069ee3b1434eb3601a64a90d6fc28df
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 16:10:37 2022 +0800

    fix

commit 69bd4750b11a6d50b1bb8bfd019c138d59439067
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 15:13:12 2022 +0800

    fix

commit 05976682f160cecc55844165c2d8c30d4d86e2a3
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 15:02:23 2022 +0800

    fix

commit 982064edb0bda05eb4cafb382884bc8333264f9f
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 14:49:39 2022 +0800

    fix

commit 0838e402b42cd4f766d223c1ba1fd6e73d16b66c
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 14:42:26 2022 +0800

    fix

commit 3dcc857d01b30a4228e09fa63210402274b03c24
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 14:37:28 2022 +0800

    fix

commit effc31179a896cb41a72853d73872feb1eadcfd5
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 14:13:10 2022 +0800

    fix

commit cbde7cf684605cfb4f71b1284fa2edb9edd7d9bd
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 14:03:43 2022 +0800

    老师端活动评测

commit 4c3ec3ac35cf785a20be281a611347296a6b5167
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 13:49:36 2022 +0800

    fix

commit ecc6eb581a1bbf663f5065775d7412761b729710
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 13:39:14 2022 +0800

    fix

commit c7a9c836b8603d59688f375fe7407f37b712a433
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 10:00:35 2022 +0800

    fix

commit 9473b11f25f5c20d227c58234b1e603ec1fada77
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 09:53:17 2022 +0800

    fix

commit 917e1e86ccae43495b7275c9e4d1fe698593a9a0
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 09:47:34 2022 +0800

    去掉挑战

commit 75cdd5d4da7cbb3a43a2b7d1a6b531ff1f3f1b62
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 09:41:13 2022 +0800

    fix2

commit 6e30eecb965d988feab395c9de738a3f9444131f
Author: skyblued <806020149@qq.com>
Date:   Wed Aug 31 09:30:32 2022 +0800

    fix

commit 33b8e2e3c568b08c58485dfb3b99c5264aff4332
Author: skyblued <806020149@qq.com>
Date:   Tue Aug 30 22:48:39 2022 +0800

    fix

commit 191cca1526fe88e67c106c4d523c60c05e4676d3
Author: skyblued <806020149@qq.com>
Date:   Tue Aug 30 22:40:27 2022 +0800

    fix

commit 18bf4f5a08a99eb10fcb40f8e73f4b8b2c9f4678
Author: skyblued <806020149@qq.com>
Date:   Tue Aug 30 22:27:33 2022 +0800

    评测排行榜
skyblued 2 年之前
父節點
當前提交
f0faf9f53e

+ 9 - 0
src/components/col-header/index.tsx

@@ -51,6 +51,15 @@ export default defineComponent({
       default: () => {}
     }
   },
+  watch:{
+    backIconColor(){
+      // 设置返回按钮颜色
+      postMessage({
+        api: 'backIconChange',
+        content: { iconStyle: this.backIconColor }
+      })
+    }
+  },
   data() {
     return {
       headerTitle: null as any,

+ 9 - 0
src/router/routes-student.ts

@@ -121,6 +121,15 @@ export default [
           title: '曲目评测活动',
           isExternal: true // 是否外部浏览器可以打开
         }
+      },
+      {
+        path: '/leaderboard',
+        component: () =>
+          import('@/student/leaderboard/index'),
+        meta: {
+          title: '曲目挑战排行榜',
+          // isExternal: true // 是否外部浏览器可以打开
+        }
       }
     ]
   },

+ 9 - 0
src/router/routes-teacher.ts

@@ -272,6 +272,15 @@ export default [
         meta: {
           title: '曲目评测活动'
         }
+      },
+      {
+        path: '/leaderboard',
+        component: () =>
+          import('@/teacher/leaderboard/index'),
+        meta: {
+          title: '曲目挑战排行榜',
+          // isExternal: true // 是否外部浏览器可以打开
+        }
       }
     ]
   },

二進制
src/student/leaderboard/image/icon-emtry.png


二進制
src/student/leaderboard/image/icon-trophy.png


+ 145 - 0
src/student/leaderboard/index.module.less

@@ -0,0 +1,145 @@
+.headImg {
+  display: flex;
+}
+.tabs {
+  margin-top: -42px;
+  :global {
+    .van-tabs__wrap {
+      height: 42px;
+    }
+    .van-tabs__nav {
+      background-color: rgba(0, 0, 0, 0.68);
+      backdrop-filter: blur(10px);
+      -webkit-backdrop-filter: blur(10px);
+    }
+    .van-tabs__line {
+      background-color: transparent !important;
+      height: 0;
+      width: 0;
+      border: 8px solid transparent;
+      border-bottom-color: #fff;
+      border-radius: 0;
+    }
+    .van-empty__image {
+      width: 100px;
+      height: 114px;
+    }
+  }
+}
+.tabContent {
+  position: relative;
+  padding: 12px;
+  box-sizing: border-box;
+  overflow-y: auto;
+}
+.tabContent.hasUser {
+  padding-bottom: 80px;
+}
+.itemContent {
+  padding: 12px;
+  border-radius: 12px;
+  background-color: #fff;
+  min-height: 100%;
+  box-sizing: border-box;
+}
+.item {
+  display: flex;
+  padding: 10px 0;
+  box-sizing: border-box;
+  align-items: center;
+  .left {
+    width: 32px;
+    margin: 0 21px 0 2px;
+    text-align: center;
+    font-weight: bold;
+  }
+  .center {
+    display: flex;
+    align-items: center;
+  }
+  .right {
+    margin-left: auto;
+    text-align: right;
+    .fraction {
+      font-size: 14px;
+      font-weight: 600;
+      color: #fa6400;
+      margin-bottom: 4px;
+    }
+    .time {
+      font-size: 12px;
+      color: #999;
+    }
+  }
+  &:first-child {
+    padding-top: 0;
+    border-bottom: 1px solid #eee;
+  }
+  &:last-child {
+    padding-bottom: 0;
+  }
+  .user {
+    margin-left: 6px;
+    .userContent{
+      display: flex;
+      align-items: center;
+      margin-bottom: 4px;
+    }
+    .name {
+      font-size: 15px;
+      color: #333;
+      margin-right: 6px;
+    }
+    .tag {
+      font-size: 12px;
+      background-color: #ffe2b2;
+      color: #ff8c00;
+      border-radius: 4px;
+      margin-right: 4px;
+      padding: 1px 2px;
+    }
+    .times{
+      font-size: 12px;
+      color: #999;
+    }
+  }
+}
+
+.activeUser {
+  --van-cell-line-height: 20px;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  box-sizing: border-box;
+  box-shadow: 0 -8px 12px #ebedf0;
+  background-color: #fff;
+  :global {
+    .van-cell__title {
+      font-weight: bold;
+    }
+    .van-cell__label {
+      font-weight: 400;
+    }
+    .van-cell__value {
+      flex: initial;
+      margin-left: 8px;
+    }
+  }
+  .avator {
+    width: 48px;
+    height: 48px;
+    margin-right: 12px;
+  }
+  .btn {
+    padding: 4px 10px;
+    border: none;
+    background: linear-gradient(180deg, #ffa200 0%, #ff6900 100%);
+    border-radius: 16px;
+  }
+  .num {
+    font-size: 14px;
+    font-weight: bold;
+    color: #fa6400;
+  }
+}

+ 300 - 0
src/student/leaderboard/index.tsx

@@ -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>
+    )
+  }
+})

二進制
src/student/share-active/track-review-activity/images/icon-lv.png


+ 10 - 3
src/student/share-active/track-review-activity/index.tsx

@@ -81,6 +81,13 @@ export default defineComponent({
     // 判断活动状态的活动时间
     this.checkActivityTime()
   },
+  unmounted() {
+    const visibilityChangeEvent = this.hiddenProperty.replace(
+      /hidden/i,
+      'visibilitychange'
+    )
+    document.removeEventListener(visibilityChangeEvent, this.onVisibilityChange)
+  },
   methods: {
     onVisibilityChange() {
       if (!document[this.hiddenProperty]) {
@@ -319,9 +326,9 @@ export default defineComponent({
                 活动曲目
               </span>
 
-              <span class={styles.titleTips}>
-                共{this.activityMusic.length || 0}
-                首曲目
+              <span class={styles.titleTips} onClick={() => this.$router.push({path: '/leaderboard', query:{id: this.id}})}>
+                查看挑战排行榜
+                <img style={{width: '16px', marginLeft: '4px'}} src={getAssetsHomeFile('icon-lv.png')} />
               </span>
             </h2>
 

二進制
src/student/teacher-dependent/images/icon-chat.png


+ 9 - 2
src/student/teacher-dependent/model/teacher-header.module.less

@@ -81,6 +81,11 @@
 
   .btn {
     padding: 3px 12px 1px;
+    min-width: 62px;
+  }
+  .btnStar{
+    color: #F18400;
+    border-color: #F18400;
   }
 }
 
@@ -184,13 +189,15 @@
   align-items: center;
 
   .subject {
+    display: flex;
+    align-items: center;
     margin-left: 5px;
     background: #fff1de;
     border-radius: 4px;
     font-size: 12px;
+    line-height: 12px;
     color: #ff8c00;
-    line-height: 16px;
-    padding: 0px 4px;
+    padding: 3px;
     white-space: nowrap;
     &:first-child {
       margin-left: 0;

+ 6 - 4
src/student/teacher-dependent/model/teacher-header.tsx

@@ -6,6 +6,7 @@ import iconTeacher from '@common/images/icon_teacher.png'
 import request from '@/helpers/request'
 import IconXueli from '@common/images/icon-xueli.png'
 import IconJiaozi from '@common/images/icon-jiaozi.png'
+import IconChat from '../images/icon-chat.png'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `../images/${fileName}`
@@ -163,12 +164,12 @@ export default defineComponent({
                 <Button
                   type="primary"
                   size="small"
-                  plain={!!this.userInfo.isStar}
+                  plain
                   round
-                  class={styles.btn}
+                  class={[styles.btn, this.userInfo.isStar ? styles.btnStar : '']}
                   onClick={this.onStart}
                 >
-                  {!this.userInfo.isStar && <Icon name="plus" />}
+                  {/* {!this.userInfo.isStar && <Icon name="plus" />} */}
 
                   {this.userInfo.isStar ? '已关注' : '关注'}
                 </Button>
@@ -178,6 +179,7 @@ export default defineComponent({
                   round
                   style={{ marginLeft: '5px' }}
                   class={styles.btn}
+                  icon={IconChat}
                   onClick={() => {
                     postMessage({
                       api: 'joinChatGroup',
@@ -188,7 +190,7 @@ export default defineComponent({
                     })
                   }}
                 >
-                  <Icon name="chat-o" style={{ marginRight: '3px' }} />
+                  {/* <Icon name={} size="16" style={{ marginRight: '3px' }} /> */}
                   聊天
                 </Button>
               </div>

+ 3 - 0
src/student/teacher-dependent/teacher-home.module.less

@@ -5,6 +5,9 @@
   overflow: hidden;
 
   :global {
+    .van-nav-bar{
+      transition: all .3s;
+    }
     .van-tab {
       margin-top: 12px;
       margin-bottom: 5px;

+ 35 - 14
src/student/teacher-dependent/teacher-home.tsx

@@ -30,7 +30,10 @@ export default defineComponent({
       tabs: tabs || query.tabs || 'single',
       userInfo: {} as any,
       background: 'rgba(55, 205, 177, 0)',
-      height: 'auto' as any
+      headColor: '#fff',
+      height: 'auto' as any,
+      backIconColor: 'white',
+      homeContaiterHeight: ''
     }
   },
   async created() {},
@@ -42,12 +45,17 @@ export default defineComponent({
     })
     useEventListener(document, 'scroll', evt => {
       const { y } = useWindowScroll()
-      this.background = `rgba(55, 205, 177, ${y.value / 100})`
-      // if (y.value > 45) {
-      //   this.background = '#37cdb1'
-      // } else {
-      //   this.background = 'transparent'
-      // }
+      // this.background = `rgba(255, 255, 255, ${y.value / 100})`
+      // console.log(y.value)
+      if (y.value > 142) {
+        this.headColor = '#000'
+        this.background = '#fff'
+        this.backIconColor = 'black'
+      } else {
+        this.background = 'transparent'
+        this.headColor = '#fff'
+        this.backIconColor = 'white'
+      }
     })
   },
   methods: {
@@ -69,12 +77,13 @@ export default defineComponent({
           <ColHeader
             background={this.background}
             border={false}
-            color="#fff"
-            backIconColor="white"
+            color={this.headColor}
+            backIconColor={this.backIconColor as any}
             onHeaderBack={() => {
               this.$nextTick(() => {
                 const { height } = useRect((this as any).$refs.headers)
                 this.height = height
+                this.homeContaiterHeight = `calc(100vh - var(--van-tabs-line-height) - ${height}px - 15px)`
               })
             }}
           />
@@ -97,19 +106,31 @@ export default defineComponent({
           }}
         >
           <Tab title="个人风采" name="single">
-            {this.tabs === 'single' && <Single userInfo={this.userInfo} />}
+            <div style={{minHeight: this.homeContaiterHeight}}>
+              {this.tabs === 'single' && <Single userInfo={this.userInfo} />}
+            </div>
           </Tab>
           <Tab title="陪练课" name="practice">
-            {this.tabs === 'practice' && <Practice userInfo={this.userInfo} />}
+            <div style={{minHeight: this.homeContaiterHeight}}>
+              {this.tabs === 'practice' && (
+                <Practice userInfo={this.userInfo} />
+              )}
+            </div>
           </Tab>
           <Tab title="直播课" name="live">
-            {this.tabs === 'live' && <Live />}
+            <div style={{minHeight: this.homeContaiterHeight}}>
+              {this.tabs === 'live' && <Live />}
+            </div>
           </Tab>
           <Tab title="视频课" name="video">
-            {this.tabs === 'video' && <VideoList />}
+            <div style={{minHeight: this.homeContaiterHeight}}>
+              {this.tabs === 'video' && <VideoList />}
+            </div>
           </Tab>
           <Tab title="乐谱" name="music">
-            {this.tabs === 'music' && <Music />}
+            <div style={{minHeight: this.homeContaiterHeight}}>
+              {this.tabs === 'music' && <Music />}
+            </div>
           </Tab>
         </Tabs>
 

二進制
src/teacher/leaderboard/image/icon-emtry.png


二進制
src/teacher/leaderboard/image/icon-trophy.png


+ 103 - 0
src/teacher/leaderboard/index.module.less

@@ -0,0 +1,103 @@
+.headImg {
+  display: flex;
+}
+.tabs {
+  margin-top: -42px;
+  :global {
+    .van-tabs__wrap {
+      height: 42px;
+    }
+    .van-tabs__nav {
+      background-color: rgba(0, 0, 0, 0.68);
+      backdrop-filter: blur(10px);
+      -webkit-backdrop-filter: blur(10px);
+    }
+    .van-tabs__line {
+      background-color: transparent !important;
+      height: 0;
+      width: 0;
+      border: 8px solid transparent;
+      border-bottom-color: #fff;
+      border-radius: 0;
+    }
+    .van-empty__image {
+      width: 100px;
+      height: 114px;
+    }
+  }
+}
+.tabContent {
+  position: relative;
+  padding: 12px;
+  box-sizing: border-box;
+  overflow-y: auto;
+}
+.itemContent {
+  padding: 12px;
+  border-radius: 12px;
+  background-color: #fff;
+  min-height: 100%;
+  box-sizing: border-box;
+}
+.item {
+  display: flex;
+  padding: 10px 0;
+  box-sizing: border-box;
+  align-items: center;
+  .left {
+    width: 32px;
+    margin: 0 21px 0 2px;
+    text-align: center;
+    font-weight: bold;
+  }
+  .center {
+    display: flex;
+    align-items: center;
+  }
+  .right {
+    margin-left: auto;
+    text-align: right;
+    .fraction {
+      font-size: 14px;
+      font-weight: 600;
+      color: #fa6400;
+      margin-bottom: 4px;
+    }
+    .time {
+      font-size: 12px;
+      color: #999;
+    }
+  }
+  &:first-child {
+    padding-top: 0;
+    border-bottom: 1px solid #eee;
+  }
+  &:last-child {
+    padding-bottom: 0;
+  }
+  .user {
+    margin-left: 6px;
+    .userContent{
+      display: flex;
+      align-items: center;
+      margin-bottom: 4px;
+    }
+    .name {
+      font-size: 15px;
+      color: #333;
+      margin-right: 6px;
+    }
+    .tag {
+      font-size: 12px;
+      background-color: #ffe2b2;
+      color: #ff8c00;
+      border-radius: 4px;
+      margin-right: 4px;
+      padding: 1px 2px;
+    }
+    .times{
+      font-size: 12px;
+      color: #999;
+    }
+  }
+}

+ 176 - 0
src/teacher/leaderboard/index.tsx

@@ -0,0 +1,176 @@
+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 = []
+            return n
+          })
+        }
+        img.value = subjectUrl
+      } 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 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="rgba(153,152,155,1)"
+              titleActiveColor="#fff"
+              onChange={index => getData()}
+            >
+              {state.musicList.map((item: IMusicItem) => {
+                return (
+                  <Tab title={item.musicSheetName}>
+                    <div
+                      class={[styles.tabContent, 'van-safe-area-bottom']}
+                      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>
+                  </Tab>
+                )
+              })}
+            </Tabs>
+          )}
+        </div>
+      </div>
+    )
+  }
+})

二進制
src/teacher/share-page/track-review-activity/images/icon-lv.png


+ 5 - 3
src/teacher/share-page/track-review-activity/index.tsx

@@ -154,9 +154,11 @@ export default defineComponent({
                 活动曲目
               </span>
 
-              <span class={styles.titleTips}>
-                共{this.activityMusic.length || 0}
-                首曲目
+              <span class={styles.titleTips}  onClick={() => this.$router.push({path: '/leaderboard', query:{id: this.id}})}>
+                查看挑战排行榜
+                <img style={{width: '16px', marginLeft: '4px'}} src={getAssetsHomeFile('icon-lv.png')} />
+                {/* 共{this.activityMusic.length || 0}
+                首曲目 */}
               </span>
             </h2>