Files
ojnext/src/admin/contest/list.vue
yuetsh c1c6e75a7b
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled
update
2026-05-21 19:38:24 -06:00

133 lines
3.0 KiB
Vue

<script setup lang="ts">
import { NSwitch, NTag } from "naive-ui"
import ContestTitle from "shared/components/ContestTitle.vue"
import ContestType from "shared/components/ContestType.vue"
import Pagination from "shared/components/Pagination.vue"
import { CONTEST_STATUS } from "utils/constants"
import { parseTime } from "utils/functions"
import type { Contest } from "utils/types"
import { editContest, getContestList } from "../api"
import Actions from "./components/Actions.vue"
const contests = ref<Contest[]>([])
const total = ref(0)
const query = reactive({
limit: 10,
page: 1,
keyword: "",
})
function toggleVisible(contest: Contest) {
contest.visible = !contest.visible
editContest(contest)
}
const columns: DataTableColumn<Contest>[] = [
{ title: "ID", key: "id", width: 60 },
{
title: "比赛",
key: "title",
minWidth: 200,
render: (row) => h(ContestTitle, { contest: row }),
},
{
title: "标签",
key: "tag",
width: 100,
},
{
title: "类型",
key: "contest_type",
width: 100,
render: (row) => h(ContestType, { contest: row, size: "small" }),
},
{
title: "状态",
key: "status",
width: 100,
render: (row) =>
h(
NTag,
{ type: CONTEST_STATUS[row.status]["type"], size: "small" },
() => CONTEST_STATUS[row.status]["name"],
),
},
{
title: "创建者",
key: "created_by",
width: 100,
render: (row) => row.created_by.username,
},
{
title: "创建时间",
key: "create_time",
width: 160,
render: (row) => parseTime(row.create_time, "YYYY-MM-DD HH:mm"),
},
{
title: "可见",
key: "visible",
width: 80,
render: (row) =>
h(NSwitch, {
value: row.visible,
size: "small",
rubberBand: false,
onUpdateValue: () => toggleVisible(row),
}),
},
{
title: "选项",
key: "actions",
width: 300,
render: (row) => h(Actions, { contest: row }),
},
]
async function listContests() {
const offset = (query.page - 1) * query.limit
const res = await getContestList(offset, query.limit, query.keyword)
contests.value = res.data.results
total.value = res.data.total
}
onMounted(listContests)
watch(() => [query.page, query.limit], listContests)
watchDebounced(() => query.keyword, listContests, {
debounce: 500,
maxWait: 1000,
})
</script>
<template>
<n-flex justify="space-between" class="titleWrapper">
<n-flex align="center">
<h2 class="title">比赛列表</h2>
<n-button
type="primary"
@click="$router.push({ name: 'admin contest create' })"
>
新建
</n-button>
</n-flex>
<div>
<n-input v-model:value="query.keyword" placeholder="输入标题关键字" />
</div>
</n-flex>
<n-data-table :columns="columns" :data="contests" />
<Pagination
:total="total"
v-model:limit="query.limit"
v-model:page="query.page"
/>
</template>
<style scoped>
.titleWrapper {
margin-bottom: 16px;
}
.title {
margin: 0;
}
</style>