contest list.
This commit is contained in:
@@ -40,6 +40,12 @@ export function deleteUsers(userIDs: number[]) {
|
||||
return http.delete("admin/user", { params: { id: userIDs.join(",") } })
|
||||
}
|
||||
|
||||
export function editUser(user: User & { password: string }) {
|
||||
export function editUser(user: User) {
|
||||
return http.put("admin/user", user)
|
||||
}
|
||||
|
||||
export function getContestList(offset = 0, limit = 10, keyword: string) {
|
||||
return http.get("admin/contest", {
|
||||
params: { paging: true, offset, limit, keyword },
|
||||
})
|
||||
}
|
||||
|
||||
41
src/admin/contest/components/Actions.vue
Normal file
41
src/admin/contest/components/Actions.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts" setup>
|
||||
import { Contest } from "~/utils/types"
|
||||
|
||||
interface Props {
|
||||
contest: Contest
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const router = useRouter()
|
||||
|
||||
function goEdit() {
|
||||
router.push({
|
||||
name: "admin contest edit",
|
||||
params: { contestID: props.contest.id },
|
||||
})
|
||||
}
|
||||
|
||||
function goEditProblems() {
|
||||
router.push({
|
||||
name: "admin contest problems",
|
||||
params: { contestID: props.contest.id },
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<n-space>
|
||||
<n-button size="small" type="primary" secondary @click="goEdit">
|
||||
编辑
|
||||
</n-button>
|
||||
<n-button size="small" type="info" secondary @click="goEditProblems">
|
||||
题目
|
||||
</n-button>
|
||||
<n-button size="small" type="warning" secondary>公告</n-button>
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button size="small" secondary>下载</n-button>
|
||||
</template>
|
||||
下载 AC 提交
|
||||
</n-tooltip>
|
||||
</n-space>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
@@ -1,7 +1,90 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { DataTableColumn, NSwitch, NTag } from "naive-ui"
|
||||
import Pagination from "~/shared/Pagination.vue"
|
||||
import { Contest } from "~/utils/types"
|
||||
import { getContestList } from "../api"
|
||||
import ContestType from "~/shared/ContestType.vue"
|
||||
import ContestTitle from "~/shared/ContestTitle.vue"
|
||||
import { CONTEST_STATUS } from "~/utils/constants"
|
||||
import Actions from "./components/Actions.vue"
|
||||
|
||||
const contests = ref<Contest[]>([])
|
||||
const total = ref(0)
|
||||
const query = reactive({
|
||||
limit: 10,
|
||||
page: 1,
|
||||
keyword: "",
|
||||
})
|
||||
|
||||
function toggleVisible(id: number) {}
|
||||
|
||||
const columns: DataTableColumn<Contest>[] = [
|
||||
{ title: "ID", key: "id", width: 60 },
|
||||
{
|
||||
title: "比赛",
|
||||
key: "title",
|
||||
minWidth: 250,
|
||||
render: (row) => h(ContestTitle, { contest: row }),
|
||||
},
|
||||
{
|
||||
title: "类型",
|
||||
key: "contest_type",
|
||||
width: 80,
|
||||
render: (row) => h(ContestType, { contest: row, size: "small" }),
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 80,
|
||||
render: (row) =>
|
||||
h(
|
||||
NTag,
|
||||
{ type: CONTEST_STATUS[row.status]["type"], size: "small" },
|
||||
() => CONTEST_STATUS[row.status]["name"]
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "可见",
|
||||
key: "visible",
|
||||
width: 60,
|
||||
render: (row) =>
|
||||
h(NSwitch, {
|
||||
value: row.visible,
|
||||
size: "small",
|
||||
rubberBand: false,
|
||||
onUpdateValue: () => toggleVisible(row.id),
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: "选项",
|
||||
key: "actions",
|
||||
width: 260,
|
||||
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, listContests, { deep: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>contest list</div>
|
||||
<n-form>
|
||||
<n-form-item>
|
||||
<n-input v-model:value="query.keyword" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<n-data-table :columns="columns" :data="contests" size="small" />
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:limit="query.limit"
|
||||
v-model:page="query.page"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -37,7 +37,8 @@ const columns: DataTableColumn<AdminProblemFiltered>[] = [
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "edit",
|
||||
title: "选项",
|
||||
key: "actions",
|
||||
width: 200,
|
||||
render: (row) => h(Actions, { problemID: row.id, onDeleted: listProblems }),
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@ const columns: DataTableColumn<User>[] = [
|
||||
{ title: "真名", key: "real_name", width: 100 },
|
||||
{ title: "邮箱", key: "email", width: 200 },
|
||||
{
|
||||
key: "edit",
|
||||
key: "actions",
|
||||
title: "选项",
|
||||
width: 200,
|
||||
render: (row) =>
|
||||
|
||||
8
src/components.d.ts
vendored
8
src/components.d.ts
vendored
@@ -18,12 +18,10 @@ declare module '@vue/runtime-core' {
|
||||
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
||||
IEpSunny: typeof import('~icons/ep/sunny')['default']
|
||||
NAlert: typeof import('naive-ui')['NAlert']
|
||||
NAvatar: typeof import("naive-ui")["NAvatar"]
|
||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCode: typeof import("naive-ui")["NCode"]
|
||||
NCode: typeof import('naive-ui')['NCode']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
@@ -55,7 +53,7 @@ declare module '@vue/runtime-core' {
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
NUpload: typeof import("naive-ui")["NUpload"]
|
||||
NUpload: typeof import('naive-ui')['NUpload']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { parseTime } from "utils/functions"
|
||||
import { useContestStore } from "oj/store/contest"
|
||||
import ContestTypeVue from "./ContestType.vue"
|
||||
import ContestTypeVue from "~/shared/ContestType.vue"
|
||||
|
||||
const contestStore = useContestStore()
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { DataTableColumn, NTag, SelectOption } from "naive-ui"
|
||||
import { getContestList } from "oj/api"
|
||||
import Pagination from "~/shared/Pagination.vue"
|
||||
import { filterEmptyValue, parseTime, duration } from "utils/functions"
|
||||
import { Contest } from "utils/types"
|
||||
import { ContestType, CONTEST_STATUS } from "~/utils/constants"
|
||||
import ContestTitle from "./components/ContestTitle.vue"
|
||||
import ContestTitle from "~/shared/ContestTitle.vue"
|
||||
import Pagination from "~/shared/Pagination.vue"
|
||||
import { useUserStore } from "~/shared/store/user"
|
||||
import { toggleLogin } from "~/shared/composables/modal"
|
||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ContestType } from "~/utils/constants"
|
||||
|
||||
interface Props {
|
||||
contest: Contest
|
||||
size?: "small"
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@@ -14,7 +15,7 @@ const isPrivate = computed(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tag :type="isPrivate ? 'error' : 'info'">
|
||||
<n-tag :type="isPrivate ? 'error' : 'info'" :size="props.size">
|
||||
{{ isPrivate ? "需要密码" : "公开" }}
|
||||
</n-tag>
|
||||
</template>
|
||||
@@ -45,6 +45,7 @@ export interface User {
|
||||
two_factor_auth: boolean
|
||||
open_api: boolean
|
||||
is_disabled: boolean
|
||||
password?: string
|
||||
}
|
||||
|
||||
export type LANGUAGE =
|
||||
@@ -236,6 +237,7 @@ export interface Contest {
|
||||
create_time: string
|
||||
now: string
|
||||
last_update_time: string
|
||||
visible: boolean
|
||||
}
|
||||
|
||||
interface SubmissionInfo {
|
||||
|
||||
Reference in New Issue
Block a user