@@ -143,18 +143,18 @@ export function getActivityRank(start: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getClassRank(
|
export function getClassRank(grade?: number | null) {
|
||||||
offset: number,
|
|
||||||
limit: number,
|
|
||||||
grade?: number | null,
|
|
||||||
) {
|
|
||||||
return http.get("class_rank", {
|
return http.get("class_rank", {
|
||||||
params: { offset, limit, grade },
|
params: { grade },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserClassRank() {
|
export function getUserClassRank(
|
||||||
return http.get("user_class_rank")
|
scope?: "all" | "window",
|
||||||
|
offset?: number,
|
||||||
|
limit?: number,
|
||||||
|
) {
|
||||||
|
return http.get("user_class_rank", { params: { scope, offset, limit } })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getClassPK(
|
export function getClassPK(
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { getUserClassRank } from "oj/api"
|
|
||||||
import { useUserStore } from "shared/store/user"
|
|
||||||
import { NButton } from "naive-ui"
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const router = useRouter()
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
interface UserRank {
|
|
||||||
rank: number
|
|
||||||
username: string
|
|
||||||
accepted_number: number
|
|
||||||
submission_number: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const myRank = ref(-1)
|
|
||||||
const className = ref("")
|
|
||||||
const data = ref<UserRank[]>([])
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
const columns: DataTableColumn<UserRank>[] = [
|
|
||||||
{
|
|
||||||
title: "排名",
|
|
||||||
key: "rank",
|
|
||||||
width: 100,
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "用户名",
|
|
||||||
key: "username",
|
|
||||||
width: 200,
|
|
||||||
render: (row) =>
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
text: true,
|
|
||||||
type: "info",
|
|
||||||
onClick: () => router.push("/user?name=" + row.username),
|
|
||||||
},
|
|
||||||
() => row.username,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "AC数",
|
|
||||||
key: "accepted_number",
|
|
||||||
width: 120,
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "提交数",
|
|
||||||
key: "submission_number",
|
|
||||||
width: 120,
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
if (!userStore.user) {
|
|
||||||
await userStore.getMyProfile()
|
|
||||||
}
|
|
||||||
const user = userStore.user
|
|
||||||
if (!user || !user.class_name) {
|
|
||||||
message.warning("您没有班级信息")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getUserClassRank()
|
|
||||||
myRank.value = res.data.my_rank
|
|
||||||
className.value = res.data.class_name
|
|
||||||
data.value = res.data.ranks
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(init)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<n-flex vertical size="large" v-if="!loading">
|
|
||||||
<n-h2>我的班级排名</n-h2>
|
|
||||||
<n-alert v-if="className" type="info">
|
|
||||||
班级:{{ className }} | 我的排名:{{
|
|
||||||
myRank > 0 ? `第${myRank}名` : "暂无排名"
|
|
||||||
}}
|
|
||||||
| 班级总人数:{{ data.length }}
|
|
||||||
</n-alert>
|
|
||||||
<n-alert v-else type="warning"> 您还没有加入班级 </n-alert>
|
|
||||||
|
|
||||||
<n-data-table :data="data" :columns="columns" />
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { getClassRank } from "oj/api"
|
|
||||||
import Pagination from "shared/components/Pagination.vue"
|
|
||||||
|
|
||||||
interface ClassRank {
|
|
||||||
rank: number
|
|
||||||
class_name: string
|
|
||||||
user_count: number
|
|
||||||
total_ac: number
|
|
||||||
total_submission: number
|
|
||||||
avg_ac: number
|
|
||||||
ac_rate: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = ref<ClassRank[]>([])
|
|
||||||
const total = ref(0)
|
|
||||||
const query = reactive({
|
|
||||||
limit: 10,
|
|
||||||
page: 1,
|
|
||||||
grade: null as number | null,
|
|
||||||
})
|
|
||||||
|
|
||||||
const gradeOptions = [
|
|
||||||
{ label: "24年级", value: 24 },
|
|
||||||
{ label: "23年级", value: 23 },
|
|
||||||
{ label: "22年级", value: 22 },
|
|
||||||
{ label: "21年级", value: 21 },
|
|
||||||
{ label: "20年级", value: 20 },
|
|
||||||
]
|
|
||||||
|
|
||||||
const columns: DataTableColumn<ClassRank>[] = [
|
|
||||||
{
|
|
||||||
title: "排名",
|
|
||||||
key: "rank",
|
|
||||||
width: 100,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
render: (row) => {
|
|
||||||
if (row.rank === 1) return "🥇"
|
|
||||||
if (row.rank === 2) return "🥈"
|
|
||||||
if (row.rank === 3) return "🥉"
|
|
||||||
return row.rank
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "班级",
|
|
||||||
key: "class_name",
|
|
||||||
width: 200,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "人数",
|
|
||||||
key: "user_count",
|
|
||||||
width: 100,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "总AC数",
|
|
||||||
key: "total_ac",
|
|
||||||
width: 120,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "总提交数",
|
|
||||||
key: "total_submission",
|
|
||||||
width: 120,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "平均AC数",
|
|
||||||
key: "avg_ac",
|
|
||||||
width: 120,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "正确率",
|
|
||||||
key: "ac_rate",
|
|
||||||
width: 100,
|
|
||||||
titleAlign: "center",
|
|
||||||
align: "center",
|
|
||||||
render: (row) => `${row.ac_rate}%`,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
if (query.grade === null) {
|
|
||||||
data.value = []
|
|
||||||
total.value = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const offset = (query.page - 1) * query.limit
|
|
||||||
const res = await getClassRank(offset, query.limit, query.grade)
|
|
||||||
data.value = res.data.results
|
|
||||||
total.value = res.data.total
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => query.page, init)
|
|
||||||
watch(
|
|
||||||
() => query.limit,
|
|
||||||
() => {
|
|
||||||
query.page = 1
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
watch(
|
|
||||||
() => query.grade,
|
|
||||||
() => {
|
|
||||||
query.page = 1
|
|
||||||
init()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (query.grade !== null) {
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<n-flex justify="center">
|
|
||||||
<n-h2>班级排名</n-h2>
|
|
||||||
</n-flex>
|
|
||||||
<n-flex justify="center" style="margin-bottom: 16px">
|
|
||||||
<n-select
|
|
||||||
v-model:value="query.grade"
|
|
||||||
placeholder="选择年级"
|
|
||||||
clearable
|
|
||||||
style="width: 200px"
|
|
||||||
:options="gradeOptions"
|
|
||||||
/>
|
|
||||||
</n-flex>
|
|
||||||
<n-data-table :data="data" :columns="columns" />
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="query.page"
|
|
||||||
v-model:limit="query.limit"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatISO, sub, type Duration } from "date-fns"
|
import { formatISO, sub, type Duration } from "date-fns"
|
||||||
import { NButton } from "naive-ui"
|
import { NButton, NFlex, useThemeVars } from "naive-ui"
|
||||||
import { getActivityRank, getRank } from "oj/api"
|
import {
|
||||||
|
getActivityRank,
|
||||||
|
getClassRank,
|
||||||
|
getRank,
|
||||||
|
getUserClassRank,
|
||||||
|
} from "oj/api"
|
||||||
|
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||||
import { getACRate } from "utils/functions"
|
import { getACRate } from "utils/functions"
|
||||||
import { Rank } from "utils/types"
|
import { Rank } from "utils/types"
|
||||||
import Pagination from "shared/components/Pagination.vue"
|
import Pagination from "shared/components/Pagination.vue"
|
||||||
@@ -9,17 +15,60 @@ import { ChartType } from "utils/constants"
|
|||||||
import { renderTableTitle } from "utils/renders"
|
import { renderTableTitle } from "utils/renders"
|
||||||
import Chart from "./components/Chart.vue"
|
import Chart from "./components/Chart.vue"
|
||||||
import Index from "./components/Index.vue"
|
import Index from "./components/Index.vue"
|
||||||
|
import { useUserStore } from "shared/store/user"
|
||||||
|
import { Icon } from "@iconify/vue"
|
||||||
|
|
||||||
|
const gradeOptions = [
|
||||||
|
{ label: "24年级", value: 24 },
|
||||||
|
{ label: "23年级", value: 23 },
|
||||||
|
{ label: "22年级", value: 22 },
|
||||||
|
{ label: "21年级", value: 21 },
|
||||||
|
{ label: "20年级", value: 20 },
|
||||||
|
]
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const themeVars = useThemeVars()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const { isDesktop } = useBreakpoints()
|
||||||
const data = ref<Rank[]>([])
|
const data = ref<Rank[]>([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
limit: 10,
|
limit: 10,
|
||||||
page: 1,
|
page: 1,
|
||||||
})
|
})
|
||||||
const chart = ref<Rank[]>([])
|
const rankChart = ref<Rank[]>([])
|
||||||
const chartType = ref(ChartType.Rank)
|
const activityChart = ref<Rank[]>([])
|
||||||
const duration = ref("weeks:1")
|
const duration = ref("months:1")
|
||||||
|
const classData = ref<ClassRank[]>([])
|
||||||
|
const classQuery = reactive({
|
||||||
|
grade: gradeOptions[0].value,
|
||||||
|
})
|
||||||
|
const myClassData = ref<UserRank[]>([])
|
||||||
|
const myRank = ref(-1)
|
||||||
|
const myClassName = ref("")
|
||||||
|
const myClassScope = ref<"window" | "all">("window")
|
||||||
|
const myClassTotal = ref(0)
|
||||||
|
const myClassQuery = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
interface ClassRank {
|
||||||
|
rank: number
|
||||||
|
class_name: string
|
||||||
|
user_count: number
|
||||||
|
total_ac: number
|
||||||
|
total_submission: number
|
||||||
|
avg_ac: number
|
||||||
|
ac_rate: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserRank {
|
||||||
|
rank: number
|
||||||
|
username: string
|
||||||
|
accepted_number: number
|
||||||
|
submission_number: number
|
||||||
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const offset = (query.page - 1) * query.limit
|
const offset = (query.page - 1) * query.limit
|
||||||
@@ -96,24 +145,23 @@ watch(
|
|||||||
watch(duration, listActivity)
|
watch(duration, listActivity)
|
||||||
|
|
||||||
async function listActivity() {
|
async function listActivity() {
|
||||||
chartType.value = ChartType.Activity
|
|
||||||
const current = Date.now()
|
const current = Date.now()
|
||||||
const start = formatISO(sub(current, subOptions.value))
|
const start = formatISO(sub(current, subOptions.value))
|
||||||
const res = await getActivityRank(start)
|
const res = await getActivityRank(start)
|
||||||
chart.value = res.data.map((d: { username: string; count: number }) => ({
|
activityChart.value = res.data.map(
|
||||||
user: {
|
(d: { username: string; count: number }) => ({
|
||||||
username: d.username,
|
user: {
|
||||||
},
|
username: d.username,
|
||||||
accepted_number: d.count,
|
},
|
||||||
submission_number: 0,
|
accepted_number: d.count,
|
||||||
}))
|
submission_number: 0,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listRank() {
|
async function listRank() {
|
||||||
chartType.value = ChartType.Rank
|
|
||||||
const res = await getRank(0, 10, 10)
|
const res = await getRank(0, 10, 10)
|
||||||
data.value = res.data.results
|
rankChart.value = res.data.results
|
||||||
chart.value = data.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const options: SelectOption[] = [
|
const options: SelectOption[] = [
|
||||||
@@ -125,51 +173,279 @@ const options: SelectOption[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const subOptions = computed<Duration>(() => {
|
const subOptions = computed<Duration>(() => {
|
||||||
let dur = options.find((it) => it.value === duration.value) ?? options[0]
|
let dur = options.find((it) => it.value === duration.value) ?? options[1]
|
||||||
const x = dur.value!.toString().split(":")
|
const x = dur.value!.toString().split(":")
|
||||||
const unit = x[0]
|
const unit = x[0]
|
||||||
const n = x[1]
|
const n = x[1]
|
||||||
return { [unit]: parseInt(n) }
|
return { [unit]: parseInt(n) }
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(() => {
|
||||||
chart.value = await init()
|
init()
|
||||||
|
listRank()
|
||||||
|
listActivity()
|
||||||
|
listClassRank()
|
||||||
|
listMyClassRank()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const classColumns: DataTableColumn<ClassRank>[] = [
|
||||||
|
{
|
||||||
|
title: "排名",
|
||||||
|
key: "rank",
|
||||||
|
width: 100,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
render: (row) => {
|
||||||
|
if (row.rank === 1) return "🥇"
|
||||||
|
if (row.rank === 2) return "🥈"
|
||||||
|
if (row.rank === 3) return "🥉"
|
||||||
|
return row.rank
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "班级",
|
||||||
|
key: "class_name",
|
||||||
|
width: 200,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "人数",
|
||||||
|
key: "user_count",
|
||||||
|
width: 100,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "总AC数",
|
||||||
|
key: "total_ac",
|
||||||
|
width: 120,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "总提交数",
|
||||||
|
key: "total_submission",
|
||||||
|
width: 120,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "平均AC数",
|
||||||
|
key: "avg_ac",
|
||||||
|
width: 120,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "正确率",
|
||||||
|
key: "ac_rate",
|
||||||
|
width: 100,
|
||||||
|
titleAlign: "center",
|
||||||
|
align: "center",
|
||||||
|
render: (row) => `${row.ac_rate}%`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const myClassColumns: DataTableColumn<UserRank>[] = [
|
||||||
|
{
|
||||||
|
title: "排名",
|
||||||
|
key: "rank",
|
||||||
|
width: 100,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "用户名",
|
||||||
|
key: "username",
|
||||||
|
width: 200,
|
||||||
|
render: (row) =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
type: "info",
|
||||||
|
onClick: () => router.push("/user?name=" + row.username),
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
row.rank === myRank.value
|
||||||
|
? h(
|
||||||
|
NFlex,
|
||||||
|
{ align: "flex-end" },
|
||||||
|
{
|
||||||
|
default: () => [
|
||||||
|
h("span", {}, row.username),
|
||||||
|
h(Icon, {
|
||||||
|
width: 20,
|
||||||
|
icon: "fluent-emoji:person-raising-hand",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: row.username,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "已解决",
|
||||||
|
key: "accepted_number",
|
||||||
|
width: 120,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "提交数",
|
||||||
|
key: "submission_number",
|
||||||
|
width: 120,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
async function listClassRank() {
|
||||||
|
if (!userStore.user) {
|
||||||
|
await userStore.getMyProfile()
|
||||||
|
}
|
||||||
|
const className = userStore.user?.class_name
|
||||||
|
if (className) {
|
||||||
|
classQuery.grade = parseInt(className.slice(0, 2))
|
||||||
|
}
|
||||||
|
const res = await getClassRank(classQuery.grade)
|
||||||
|
classData.value = res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listMyClassRank() {
|
||||||
|
try {
|
||||||
|
const offset =
|
||||||
|
myClassScope.value === "all"
|
||||||
|
? (myClassQuery.page - 1) * myClassQuery.limit
|
||||||
|
: 0
|
||||||
|
const limit = myClassScope.value === "all" ? myClassQuery.limit : undefined
|
||||||
|
const res = await getUserClassRank(myClassScope.value, offset, limit)
|
||||||
|
myRank.value = res.data.my_rank
|
||||||
|
myClassName.value = res.data.class_name
|
||||||
|
myClassData.value = res.data.ranks
|
||||||
|
myClassTotal.value = res.data.total ?? res.data.ranks.length
|
||||||
|
if (myClassScope.value === "window") {
|
||||||
|
myClassQuery.page = 1
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => classQuery.grade,
|
||||||
|
() => {
|
||||||
|
listClassRank()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(myClassScope, listMyClassRank)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => myClassQuery.page,
|
||||||
|
() => {
|
||||||
|
if (myClassScope.value === "all") {
|
||||||
|
listMyClassRank()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => myClassQuery.limit,
|
||||||
|
() => {
|
||||||
|
myClassQuery.page = 1
|
||||||
|
if (myClassScope.value === "all") {
|
||||||
|
listMyClassRank()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-flex justify="center">
|
<n-flex vertical size="large">
|
||||||
<n-button-group>
|
<n-grid :cols="isDesktop ? 2 : 1" :x-gap="20" :y-gap="20">
|
||||||
<n-button
|
<n-gi :span="1">
|
||||||
@click="listRank"
|
<n-card>
|
||||||
:type="chartType === ChartType.Rank ? 'primary' : 'default'"
|
<template #header>
|
||||||
>
|
<div style="height: 34px">全服 Top10</div>
|
||||||
天梯排名
|
</template>
|
||||||
</n-button>
|
<Chart
|
||||||
<n-button
|
v-if="rankChart.length"
|
||||||
@click="listActivity"
|
:type="ChartType.Rank"
|
||||||
:type="chartType === ChartType.Activity ? 'primary' : 'default'"
|
:rank-data="rankChart"
|
||||||
>
|
/>
|
||||||
活跃度排名
|
<n-empty v-else style="padding: 20px 0"></n-empty>
|
||||||
</n-button>
|
</n-card>
|
||||||
</n-button-group>
|
</n-gi>
|
||||||
<div v-if="chartType === ChartType.Activity">
|
<n-gi :span="1">
|
||||||
<n-select
|
<n-card>
|
||||||
style="width: 120px"
|
<template #header>日活 Top10</template>
|
||||||
:options="options"
|
<template #header-extra>
|
||||||
v-model:value="duration"
|
<n-select
|
||||||
/>
|
style="width: 120px"
|
||||||
</div>
|
:options="options"
|
||||||
|
v-model:value="duration"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<Chart
|
||||||
|
v-if="activityChart.length"
|
||||||
|
:type="ChartType.Activity"
|
||||||
|
:rank-data="activityChart"
|
||||||
|
/>
|
||||||
|
<n-empty v-else style="padding: 20px 0"></n-empty>
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
</n-grid>
|
||||||
|
<n-card>
|
||||||
|
<template #header>全服 Top100</template>
|
||||||
|
<n-data-table :data="data" :columns="columns" />
|
||||||
|
<template #footer>
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="query.page"
|
||||||
|
v-model:limit="query.limit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</n-card>
|
||||||
|
<n-grid :cols="isDesktop ? 2 : 1" :x-gap="20" :y-gap="20">
|
||||||
|
<n-gi :span="1">
|
||||||
|
<n-card>
|
||||||
|
<template #header>班级排名</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<n-select
|
||||||
|
v-model:value="classQuery.grade"
|
||||||
|
placeholder="选择年级"
|
||||||
|
clearable
|
||||||
|
style="width: 180px"
|
||||||
|
:options="gradeOptions"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<n-data-table :data="classData" :columns="classColumns" />
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
<n-gi :span="1">
|
||||||
|
<n-card>
|
||||||
|
<template #header>我在班级的排名</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<n-select
|
||||||
|
style="width: 180px"
|
||||||
|
:options="[
|
||||||
|
{ label: '我的位置', value: 'window' },
|
||||||
|
{ label: '全班排名', value: 'all' },
|
||||||
|
]"
|
||||||
|
v-model:value="myClassScope"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<n-data-table :data="myClassData" :columns="myClassColumns" />
|
||||||
|
<template #footer v-if="myClassScope === 'all'">
|
||||||
|
<Pagination
|
||||||
|
:total="myClassTotal"
|
||||||
|
v-model:page="myClassQuery.page"
|
||||||
|
v-model:limit="myClassQuery.limit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
</n-grid>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
<Chart v-if="!!chart.length" :type="chartType" :rank-data="chart" />
|
|
||||||
<n-empty v-else style="padding: 20px 0"></n-empty>
|
|
||||||
<n-flex justify="center">
|
|
||||||
<n-h2>全校前100名</n-h2>
|
|
||||||
</n-flex>
|
|
||||||
<n-data-table :data="data" :columns="columns" />
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="query.page"
|
|
||||||
v-model:limit="query.limit"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|||||||
@@ -68,21 +68,7 @@ export const ojs: RouteRecordRaw = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "class",
|
path: "class",
|
||||||
children: [
|
component: () => import("oj/class/pk.vue"),
|
||||||
{
|
|
||||||
path: "rank",
|
|
||||||
component: () => import("oj/class/rank.vue"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "pk",
|
|
||||||
component: () => import("oj/class/pk.vue"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "my-rank",
|
|
||||||
component: () => import("oj/class/my-rank.vue"),
|
|
||||||
meta: { requiresAuth: true },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "announcement",
|
path: "announcement",
|
||||||
|
|||||||
@@ -119,32 +119,10 @@ const menus = computed<MenuOption[]>(() => [
|
|||||||
icon: renderIcon("streamline-emojis:hibiscus"),
|
icon: renderIcon("streamline-emojis:hibiscus"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: () => "班级",
|
label: () => h(RouterLink, { to: "/class/pk" }, { default: () => "班级" }),
|
||||||
key: "class",
|
|
||||||
show: false,
|
show: false,
|
||||||
|
key: "class",
|
||||||
icon: renderIcon("twemoji:crossed-swords"),
|
icon: renderIcon("twemoji:crossed-swords"),
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: () =>
|
|
||||||
h(RouterLink, { to: "/class/rank" }, { default: () => "班级排名" }),
|
|
||||||
key: "class-rank",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: () =>
|
|
||||||
h(RouterLink, { to: "/class/pk" }, { default: () => "班级PK" }),
|
|
||||||
key: "class-pk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: () =>
|
|
||||||
h(
|
|
||||||
RouterLink,
|
|
||||||
{ to: "/class/my-rank" },
|
|
||||||
{ default: () => "我的排名" },
|
|
||||||
),
|
|
||||||
key: "my-rank",
|
|
||||||
show: userStore.isAuthed,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
|
|||||||
Reference in New Issue
Block a user