contest list.

This commit is contained in:
2023-03-20 20:54:12 +08:00
parent efbc21ba18
commit 4af5a28c03
11 changed files with 146 additions and 14 deletions

View File

@@ -40,6 +40,12 @@ export function deleteUsers(userIDs: number[]) {
return http.delete("admin/user", { params: { id: userIDs.join(",") } }) 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) 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 },
})
}

View 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>

View File

@@ -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> <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> </template>
<style scoped></style> <style scoped></style>

View File

@@ -37,7 +37,8 @@ const columns: DataTableColumn<AdminProblemFiltered>[] = [
}), }),
}, },
{ {
key: "edit", title: "选项",
key: "actions",
width: 200, width: 200,
render: (row) => h(Actions, { problemID: row.id, onDeleted: listProblems }), render: (row) => h(Actions, { problemID: row.id, onDeleted: listProblems }),
}, },

View File

@@ -54,7 +54,7 @@ const columns: DataTableColumn<User>[] = [
{ title: "真名", key: "real_name", width: 100 }, { title: "真名", key: "real_name", width: 100 },
{ title: "邮箱", key: "email", width: 200 }, { title: "邮箱", key: "email", width: 200 },
{ {
key: "edit", key: "actions",
title: "选项", title: "选项",
width: 200, width: 200,
render: (row) => render: (row) =>

8
src/components.d.ts vendored
View File

@@ -18,12 +18,10 @@ declare module '@vue/runtime-core' {
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default'] IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
IEpSunny: typeof import('~icons/ep/sunny')['default'] IEpSunny: typeof import('~icons/ep/sunny')['default']
NAlert: typeof import('naive-ui')['NAlert'] NAlert: typeof import('naive-ui')['NAlert']
NAvatar: typeof import("naive-ui")["NAvatar"] NAvatar: typeof import('naive-ui')['NAvatar']
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton'] NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard'] NCard: typeof import('naive-ui')['NCard']
NCode: typeof import("naive-ui")["NCode"] NCode: typeof import('naive-ui')['NCode']
NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable'] NDataTable: typeof import('naive-ui')['NDataTable']
NDescriptions: typeof import('naive-ui')['NDescriptions'] NDescriptions: typeof import('naive-ui')['NDescriptions']
@@ -55,7 +53,7 @@ declare module '@vue/runtime-core' {
NTabs: typeof import('naive-ui')['NTabs'] NTabs: typeof import('naive-ui')['NTabs']
NTag: typeof import('naive-ui')['NTag'] NTag: typeof import('naive-ui')['NTag']
NTooltip: typeof import('naive-ui')['NTooltip'] NTooltip: typeof import('naive-ui')['NTooltip']
NUpload: typeof import("naive-ui")["NUpload"] NUpload: typeof import('naive-ui')['NUpload']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
} }

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { parseTime } from "utils/functions" import { parseTime } from "utils/functions"
import { useContestStore } from "oj/store/contest" import { useContestStore } from "oj/store/contest"
import ContestTypeVue from "./ContestType.vue" import ContestTypeVue from "~/shared/ContestType.vue"
const contestStore = useContestStore() const contestStore = useContestStore()
</script> </script>

View File

@@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { DataTableColumn, NTag, SelectOption } from "naive-ui" import { DataTableColumn, NTag, SelectOption } from "naive-ui"
import { getContestList } from "oj/api" import { getContestList } from "oj/api"
import Pagination from "~/shared/Pagination.vue"
import { filterEmptyValue, parseTime, duration } from "utils/functions" import { filterEmptyValue, parseTime, duration } from "utils/functions"
import { Contest } from "utils/types" import { Contest } from "utils/types"
import { ContestType, CONTEST_STATUS } from "~/utils/constants" 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 { useUserStore } from "~/shared/store/user"
import { toggleLogin } from "~/shared/composables/modal" import { toggleLogin } from "~/shared/composables/modal"
import { isDesktop } from "~/shared/composables/breakpoints" import { isDesktop } from "~/shared/composables/breakpoints"

View File

@@ -4,6 +4,7 @@ import { ContestType } from "~/utils/constants"
interface Props { interface Props {
contest: Contest contest: Contest
size?: "small"
} }
const props = defineProps<Props>() const props = defineProps<Props>()
@@ -14,7 +15,7 @@ const isPrivate = computed(
</script> </script>
<template> <template>
<n-tag :type="isPrivate ? 'error' : 'info'"> <n-tag :type="isPrivate ? 'error' : 'info'" :size="props.size">
{{ isPrivate ? "需要密码" : "公开" }} {{ isPrivate ? "需要密码" : "公开" }}
</n-tag> </n-tag>
</template> </template>

View File

@@ -45,6 +45,7 @@ export interface User {
two_factor_auth: boolean two_factor_auth: boolean
open_api: boolean open_api: boolean
is_disabled: boolean is_disabled: boolean
password?: string
} }
export type LANGUAGE = export type LANGUAGE =
@@ -236,6 +237,7 @@ export interface Contest {
create_time: string create_time: string
now: string now: string
last_update_time: string last_update_time: string
visible: boolean
} }
interface SubmissionInfo { interface SubmissionInfo {