422 lines
8.7 KiB
Vue
422 lines
8.7 KiB
Vue
<template>
|
|
<div class="ranking-list">
|
|
<SubPageHeader>
|
|
<template #center>
|
|
<nav class="top-tabs">
|
|
<button
|
|
v-for="tab in topTabs"
|
|
:key="tab"
|
|
class="top-tab"
|
|
:class="{ active: tab === activeTab }"
|
|
type="button"
|
|
@click="activeTab = tab"
|
|
>
|
|
{{ tab }}
|
|
</button>
|
|
</nav>
|
|
</template>
|
|
</SubPageHeader>
|
|
|
|
<main class="ranking-body">
|
|
<section class="ranking-panel">
|
|
<div class="ranking-toolbar">
|
|
<LmFilterTabs v-model="metricType" :options="metricOptions" mode="compact" />
|
|
<LmFilterTabs v-model="scopeType" :options="scopeOptions" mode="compact" />
|
|
</div>
|
|
|
|
<div class="ranking-table-card">
|
|
<div class="ranking-scroll">
|
|
<table class="ranking-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="rank-col">排名</th>
|
|
<th>姓名</th>
|
|
<th class="metric-col">{{ metricLabel }}</th>
|
|
<th class="check-col"></th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<tr v-for="student in rankingRows" :key="student.rank" :class="`rank-${student.rank}`">
|
|
<td class="rank-col">
|
|
<span v-if="student.rank <= 3" class="rank-medal" :class="`medal-${student.rank}`">
|
|
{{ student.rank }}
|
|
</span>
|
|
<span v-else class="rank-number">{{ student.rank }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="student-info">
|
|
<span class="animal-avatar">{{ student.animal }}</span>
|
|
<span class="name-card">
|
|
<span class="student-name">{{ student.name }}</span>
|
|
</span>
|
|
<span class="gender-icon">♀</span>
|
|
</div>
|
|
</td>
|
|
<td>{{ student.value }}</td>
|
|
<td class="check-col">
|
|
<span class="fake-checkbox"></span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<table class="ranking-table current-table">
|
|
<tbody>
|
|
<tr>
|
|
<td class="rank-col">100</td>
|
|
<td>
|
|
<div class="student-info">
|
|
<span class="animal-avatar">{{ currentStudent.animal }}</span>
|
|
<span class="name-card">
|
|
<span class="student-name">{{ currentStudent.name }}</span>
|
|
</span>
|
|
<span class="gender-icon">♀</span>
|
|
</div>
|
|
</td>
|
|
<td class="metric-col">{{ currentStudent.value }}</td>
|
|
<td class="check-col"></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref } from 'vue'
|
|
import SubPageHeader from '@/components/layout/SubPageHeader.vue'
|
|
import LmFilterTabs, { type FilterTabOption } from '@/components/lm/LmFilterTabs.vue'
|
|
|
|
type RankingRow = {
|
|
rank: number
|
|
name: string
|
|
animal: string
|
|
value: number
|
|
}
|
|
|
|
const topTabs = ['综合', '语感', '单词', '乐贝']
|
|
const activeTab = ref('语感')
|
|
const metricType = ref('sentence')
|
|
const scopeType = ref('global')
|
|
|
|
const metricOptions: FilterTabOption[] = [
|
|
{ key: 'sentence', label: '句子数' },
|
|
{ key: 'vocabulary', label: '阅读词汇量' },
|
|
]
|
|
|
|
const scopeOptions: FilterTabOption[] = [
|
|
{ key: 'global', label: '全服' },
|
|
{ key: 'region', label: '地区' },
|
|
{ key: 'school', label: '本校' },
|
|
]
|
|
|
|
const metricLabel = computed(() => {
|
|
return metricOptions.find(option => option.key === metricType.value)?.label ?? '句子数'
|
|
})
|
|
|
|
const animalIcons = ['🐵', '🐨', '🐻', '🦁', '🐯', '🦊', '🐒']
|
|
|
|
const rankingRows: RankingRow[] = animalIcons.map((animal, index) => ({
|
|
rank: index + 1,
|
|
name: '张小小',
|
|
animal,
|
|
value: 581,
|
|
}))
|
|
|
|
const currentStudent: RankingRow = {
|
|
rank: 100,
|
|
name: '张小小',
|
|
animal: '🐶',
|
|
value: 581,
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.ranking-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
min-height: 0;
|
|
}
|
|
|
|
.top-tabs {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 56px;
|
|
}
|
|
|
|
.top-tab {
|
|
position: relative;
|
|
border: none;
|
|
background: transparent;
|
|
color: #111827;
|
|
cursor: pointer;
|
|
font-size: 36px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
padding: 8px 0 12px;
|
|
transition: color 0.2s;
|
|
|
|
&.active {
|
|
color: #2d6fe0;
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: 0;
|
|
width: 34px;
|
|
height: 6px;
|
|
border-radius: 999px;
|
|
background: #3d78f2;
|
|
transform: translateX(-50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.ranking-body {
|
|
flex: 1;
|
|
min-height: 0;
|
|
padding: 0 64px 36px;
|
|
}
|
|
|
|
.ranking-panel {
|
|
width: 100%;
|
|
max-width: 1792px;
|
|
height: 100%;
|
|
margin: 0 auto;
|
|
overflow: hidden;
|
|
border: 2px solid rgba(255, 255, 255, 0.62);
|
|
border-radius: 20px;
|
|
background: rgba(255, 255, 255, 0.28);
|
|
}
|
|
|
|
.ranking-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 52px;
|
|
height: 98px;
|
|
padding: 0 34px;
|
|
|
|
:deep(.lm-filter-tabs) {
|
|
background: rgba(230, 235, 245, 0.78);
|
|
}
|
|
|
|
:deep(.filter-btn) {
|
|
font-size: 20px;
|
|
padding: 5px 16px;
|
|
}
|
|
}
|
|
|
|
.ranking-table-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: calc(100% - 98px);
|
|
overflow: hidden;
|
|
border-radius: 18px;
|
|
background: rgba(255, 255, 255, 0.88);
|
|
}
|
|
|
|
.ranking-scroll {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow: auto;
|
|
scrollbar-width: none;
|
|
|
|
&::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.ranking-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
|
|
th,
|
|
td {
|
|
height: 104px;
|
|
border-bottom: 1px solid rgba(225, 230, 239, 0.52);
|
|
color: #1f2937;
|
|
font-size: 26px;
|
|
font-weight: 400;
|
|
text-align: left;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
th {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 2;
|
|
height: 58px;
|
|
color: #7d8796;
|
|
font-size: 22px;
|
|
background: rgba(247, 249, 253, 0.86);
|
|
}
|
|
|
|
th:nth-child(2),
|
|
td:nth-child(2) {
|
|
width: 48%;
|
|
}
|
|
|
|
th:nth-child(3),
|
|
td:nth-child(3) {
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.metric-col{
|
|
width: 820px;
|
|
}
|
|
|
|
.rank-col {
|
|
width: 410px;
|
|
padding-left: 58px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
th.rank-col {
|
|
padding-left: 62px;
|
|
}
|
|
|
|
.check-col {
|
|
width: 104px;
|
|
text-align: center;
|
|
}
|
|
|
|
.rank-1 {
|
|
background: linear-gradient(90deg, rgba(255, 246, 166, 0.5), rgba(255, 255, 255, 0.8) 62%);
|
|
}
|
|
|
|
.rank-2 {
|
|
background: linear-gradient(90deg, rgba(224, 231, 255, 0.6), rgba(255, 255, 255, 0.8) 62%);
|
|
}
|
|
|
|
.rank-3 {
|
|
background: linear-gradient(90deg, rgba(255, 229, 203, 0.5), rgba(255, 255, 255, 0.8) 62%);
|
|
}
|
|
|
|
.rank-number {
|
|
color: #333;
|
|
font-size: 26px;
|
|
}
|
|
|
|
.rank-medal {
|
|
display: inline-flex;
|
|
width: 34px;
|
|
height: 34px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
color: #8a6d13;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.55);
|
|
}
|
|
|
|
.medal-1 {
|
|
background: linear-gradient(180deg, #ffef7d, #f6c844);
|
|
}
|
|
|
|
.medal-2 {
|
|
background: linear-gradient(180deg, #e8ebf8, #bdc5df);
|
|
}
|
|
|
|
.medal-3 {
|
|
background: linear-gradient(180deg, #f5dbad, #d3aa67);
|
|
}
|
|
|
|
.student-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24px;
|
|
}
|
|
|
|
.animal-avatar {
|
|
display: inline-flex;
|
|
width: 54px;
|
|
height: 54px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
background: #fff;
|
|
font-size: 36px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.name-card {
|
|
position: relative;
|
|
display: inline-flex;
|
|
width: 250px;
|
|
height: 58px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 3px solid rgba(246, 151, 196, 0.78);
|
|
border-radius: 999px;
|
|
background: #fff8fb;
|
|
box-shadow:
|
|
inset 0 0 0 2px rgba(255, 255, 255, 0.95),
|
|
0 1px 0 rgba(255, 255, 255, 0.9);
|
|
|
|
&::before,
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 50%;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
&::before {
|
|
left: 16px;
|
|
background: #9be7ff;
|
|
}
|
|
|
|
&::after {
|
|
right: 16px;
|
|
background: #ffe68a;
|
|
}
|
|
}
|
|
|
|
.student-name {
|
|
color: #172033;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.gender-icon {
|
|
color: #ef7aa9;
|
|
font-size: 22px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.fake-checkbox {
|
|
display: inline-block;
|
|
width: 42px;
|
|
height: 42px;
|
|
border: 2px solid rgba(218, 226, 238, 0.72);
|
|
background: rgba(245, 247, 250, 0.88);
|
|
}
|
|
|
|
.current-table {
|
|
flex-shrink: 0;
|
|
background: rgba(242, 247, 255, 0.9);
|
|
box-shadow: 0 -18px 26px rgba(236, 244, 255, 0.9);
|
|
|
|
td {
|
|
height: 104px;
|
|
border-bottom: none;
|
|
color: #4f8bd3;
|
|
}
|
|
|
|
.rank-col {
|
|
color: #4f8bd3;
|
|
}
|
|
}
|
|
</style>
|