This commit is contained in:
2025-10-04 12:10:55 +08:00
parent 68512d6d54
commit a823cb7a13
7 changed files with 80 additions and 33 deletions

View File

@@ -510,7 +510,7 @@ watch(
<n-grid :cols="2" x-gap="20"> <n-grid :cols="2" x-gap="20">
<n-gi> <n-gi>
<n-form> <n-form>
<n-form-item label="本题参考答案(用于 AI 分析,不会泄露)"> <n-form-item label="本题参考答案(选填,用于 AI 分析,不会泄露)">
<n-tabs <n-tabs
type="segment" type="segment"
default-value="Python3" default-value="Python3"

View File

@@ -134,7 +134,7 @@ function rowProps(row: Contest) {
<n-form :show-feedback="false" label-placement="left" inline> <n-form :show-feedback="false" label-placement="left" inline>
<n-form-item label="比赛状态"> <n-form-item label="比赛状态">
<n-select <n-select
class="select" style="width: 120px"
:options="options" :options="options"
v-model:value="query.status" v-model:value="query.status"
/> />
@@ -146,7 +146,7 @@ function rowProps(row: Contest) {
<n-form :show-feedback="false" label-placement="left" inline> <n-form :show-feedback="false" label-placement="left" inline>
<n-form-item> <n-form-item>
<n-input <n-input
class="input" style="width: 200px"
clearable clearable
v-model:value="query.keyword" v-model:value="query.keyword"
placeholder="比赛标题" placeholder="比赛标题"
@@ -173,13 +173,3 @@ function rowProps(row: Contest) {
:total="total" :total="total"
/> />
</template> </template>
<style scoped>
.select {
width: 120px;
}
.input {
width: 200px;
}
</style>

View File

@@ -10,6 +10,7 @@ import storage from "~/utils/storage"
import { LANGUAGE } from "~/utils/types" import { LANGUAGE } from "~/utils/types"
import Submit from "./Submit.vue" import Submit from "./Submit.vue"
import StatisticsPanel from "~/shared/components/StatisticsPanel.vue" import StatisticsPanel from "~/shared/components/StatisticsPanel.vue"
import {Icon} from "@iconify/vue"
interface Props { interface Props {
storageKey: string storageKey: string
@@ -144,8 +145,10 @@ function showStatisticsPanel() {
<n-dropdown size="large" :options="menu" @select="select"> <n-dropdown size="large" :options="menu" @select="select">
<n-button :size="isDesktop ? 'medium' : 'small'">操作</n-button> <n-button :size="isDesktop ? 'medium' : 'small'">操作</n-button>
</n-dropdown> </n-dropdown>
<n-button v-if="isDesktop && userStore.isSuperAdmin" @click="goEdit"> <n-button circle v-if="isDesktop && userStore.isSuperAdmin" @click="goEdit">
编辑 <template #icon>
<Icon icon="streamline-ultimate-color:file-code-edit" />
</template>
</n-button> </n-button>
</n-flex> </n-flex>
</n-flex> </n-flex>

View File

@@ -109,7 +109,7 @@ onMounted(getBeatRate)
<n-gi v-for="item in numbers" :key="item.content"> <n-gi v-for="item in numbers" :key="item.content">
<n-card hoverable> <n-card hoverable>
<n-flex align="center"> <n-flex align="center">
<Icon :icon="item.icon" width="40" /> <Icon v-if="isDesktop" :icon="item.icon" width="40" />
<div> <div>
<n-h2 class="number"> <n-h2 class="number">
<n-number-animation <n-number-animation

View File

@@ -1,15 +1,48 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue" import { Icon } from "@iconify/vue"
defineEmits(["click", "search"]) interface Props {
type: "题目" | "用户"
username?: string
}
const props = defineProps<Props>()
const emits = defineEmits(["click", "search", "filterClass"])
const showFilterClass = computed(() => {
return props.type === "用户" && props.username?.startsWith("ks")
})
function filterClass() {
const match = props.username!.match(/^ks\d{3,4}/)
const classname = match ? match[0] : ""
if (!classname) return
emits("filterClass", classname)
}
</script> </script>
<template> <template>
<n-flex align="center"> <n-flex align="center">
<n-button text type="info" @click="$emit('click')"><slot></slot></n-button> <n-button text type="info" @click="$emit('click')"><slot></slot></n-button>
<n-button text @click="$emit('search')"> <n-tooltip>
<template #icon> <template #trigger>
<Icon icon="streamline-emojis:magnifying-glass-tilted-left"></Icon> <n-button text @click="$emit('search')">
<template #icon>
<Icon icon="streamline-emojis:magnifying-glass-tilted-left"></Icon>
</template>
</n-button>
</template> </template>
</n-button> {{ "搜索" + props.type}}
</n-tooltip>
<n-tooltip v-if="showFilterClass">
<template #trigger>
<n-button text @click="filterClass">
<template #icon>
<Icon icon="openmoji:filter"></Icon>
</template>
</n-button>
</template>
筛选班级
</n-tooltip>
</n-flex> </n-flex>
</template> </template>

View File

@@ -3,11 +3,16 @@
<n-button text type="info" @click="$emit('showCode')"> <n-button text type="info" @click="$emit('showCode')">
{{ props.submission.id.slice(0, 12) }} {{ props.submission.id.slice(0, 12) }}
</n-button> </n-button>
<n-button text @click="goto"> <n-tooltip>
<template #icon> <template #trigger>
<Icon icon="streamline-emojis:backhand-index-pointing-right-1"></Icon> <n-button text @click="goto">
<template #icon>
<Icon icon="catppuccin:folder-debug"></Icon>
</template>
</n-button>
</template> </template>
</n-button> 查看测试详情
</n-tooltip>
</n-flex> </n-flex>
<span v-else> <span v-else>
{{ props.submission.id.slice(0, 12) }} {{ props.submission.id.slice(0, 12) }}

View File

@@ -6,7 +6,7 @@ import { parseTime } from "utils/functions"
import { LANGUAGE, SubmissionListItem } from "utils/types" import { LANGUAGE, SubmissionListItem } from "utils/types"
import Pagination from "~/shared/components/Pagination.vue" import Pagination from "~/shared/components/Pagination.vue"
import SubmissionResultTag from "~/shared/components/SubmissionResultTag.vue" import SubmissionResultTag from "~/shared/components/SubmissionResultTag.vue"
import { isDesktop } from "~/shared/composables/breakpoints" import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
import { usePagination } from "~/shared/composables/pagination" import { usePagination } from "~/shared/composables/pagination"
import { useUserStore } from "~/shared/store/user" import { useUserStore } from "~/shared/store/user"
import { LANGUAGE_SHOW_VALUE } from "~/utils/constants" import { LANGUAGE_SHOW_VALUE } from "~/utils/constants"
@@ -15,6 +15,7 @@ import ButtonWithSearch from "./components/ButtonWithSearch.vue"
import StatisticsPanel from "~/shared/components/StatisticsPanel.vue" import StatisticsPanel from "~/shared/components/StatisticsPanel.vue"
import SubmissionLink from "./components/SubmissionLink.vue" import SubmissionLink from "./components/SubmissionLink.vue"
import SubmissionDetail from "./detail.vue" import SubmissionDetail from "./detail.vue"
import { Icon } from "@iconify/vue"
interface SubmissionQuery { interface SubmissionQuery {
username: string username: string
@@ -180,6 +181,7 @@ const columns = computed(() => {
h( h(
ButtonWithSearch, ButtonWithSearch,
{ {
type: "题目",
onClick: () => problemClicked(row), onClick: () => problemClicked(row),
onSearch: () => (query.problem = row.problem), onSearch: () => (query.problem = row.problem),
}, },
@@ -206,8 +208,11 @@ const columns = computed(() => {
h( h(
ButtonWithSearch, ButtonWithSearch,
{ {
type: "用户",
username: row.username,
onClick: () => window.open("/user?name=" + row.username, "_blank"), onClick: () => window.open("/user?name=" + row.username, "_blank"),
onSearch: () => (query.username = row.username), onSearch: () => (query.username = row.username),
onFilterClass: (classname: string) => (query.username = classname),
}, },
() => row.username, () => row.username,
), ),
@@ -237,6 +242,13 @@ const columns = computed(() => {
<n-flex vertical size="large"> <n-flex vertical size="large">
<n-space> <n-space>
<n-form :show-feedback="false" inline label-placement="left"> <n-form :show-feedback="false" inline label-placement="left">
<n-form-item v-if="isDesktop && userStore.isAuthed" label="只看自己">
<n-switch
v-model:value="query.myself"
checked-value="1"
unchecked-value="0"
/>
</n-form-item>
<n-form-item label="提交状态"> <n-form-item label="提交状态">
<n-select <n-select
class="select" class="select"
@@ -269,27 +281,31 @@ const columns = computed(() => {
placeholder="题号" placeholder="题号"
/> />
</n-form-item> </n-form-item>
<n-form-item v-if="userStore.isAuthed" label="只看自己"> </n-form>
<n-form :show-feedback="false" inline label-placement="left">
<n-form-item v-if="isMobile && userStore.isAuthed" label="只看自己">
<n-switch <n-switch
v-model:value="query.myself" v-model:value="query.myself"
checked-value="1" checked-value="1"
unchecked-value="0" unchecked-value="0"
/> />
</n-form-item> </n-form-item>
</n-form>
<n-form :show-feedback="false" inline label-placement="left">
<n-form-item> <n-form-item>
<n-button @click="search(query.username, query.problem)"> <n-button @click="search(query.username, query.problem)">
搜索 搜索
</n-button> </n-button>
</n-form-item> </n-form-item>
<n-form-item>
<n-button @click="clear" quaternary>重置</n-button>
</n-form-item>
<n-form-item <n-form-item
v-if="userStore.isSuperAdmin && route.name === 'submissions'" v-if="userStore.isSuperAdmin && route.name === 'submissions'"
> >
<n-button @click="toggleStatisticPanel(true)">数据统计</n-button> <n-button circle @click="toggleStatisticPanel(true)">
</n-form-item> <template #icon>
<n-form-item> <Icon icon="streamline-emojis:bar-chart" />
<n-button @click="clear" quaternary>重置</n-button> </template>
</n-button>
</n-form-item> </n-form-item>
<n-form-item v-if="todayCount > 0"> <n-form-item v-if="todayCount > 0">
<component :is="isDesktop ? NH2 : NText" class="todayCount"> <component :is="isDesktop ? NH2 : NText" class="todayCount">