refactor.
This commit is contained in:
@@ -2,9 +2,10 @@
|
|||||||
import { JUDGE_STATUS } from "../../utils/constants"
|
import { JUDGE_STATUS } from "../../utils/constants"
|
||||||
import { SUBMISSION_RESULT } from "../../utils/types"
|
import { SUBMISSION_RESULT } from "../../utils/types"
|
||||||
|
|
||||||
const { result } = defineProps<{
|
interface Props {
|
||||||
result: SUBMISSION_RESULT
|
result: SUBMISSION_RESULT
|
||||||
}>()
|
}
|
||||||
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,279 +1,41 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useTimeout, useTimeoutFn, useToggle } from "@vueuse/core"
|
import { useToggle } from "@vueuse/core"
|
||||||
import { TabsPaneContext } from "element-plus"
|
import { TabsPaneContext } from "element-plus"
|
||||||
import party from "party-js"
|
import { inject, onMounted, Ref, ref } from "vue"
|
||||||
import { computed, onMounted, ref, watch } from "vue"
|
import { Problem } from "../../../utils/types"
|
||||||
import { useRoute } from "vue-router"
|
import { submissionExists } from "../../api"
|
||||||
import {
|
import SubmitPanel from "./submit-panel.vue"
|
||||||
SOURCES,
|
|
||||||
JUDGE_STATUS,
|
|
||||||
SubmissionStatus,
|
|
||||||
} from "../../../utils/constants"
|
|
||||||
import {
|
|
||||||
submissionMemoryFormat,
|
|
||||||
submissionTimeFormat,
|
|
||||||
} from "../../../utils/functions"
|
|
||||||
import {
|
|
||||||
LANGUAGE,
|
|
||||||
Problem,
|
|
||||||
Submission,
|
|
||||||
SubmitCodePayload,
|
|
||||||
} from "../../../utils/types"
|
|
||||||
import { getSubmission, submissionExists, submitCode } from "../../api"
|
|
||||||
|
|
||||||
import SubmissionResultTag from "../../components/submission-result-tag.vue"
|
const tab = ref("testcase")
|
||||||
|
const submitPanelRef = ref<{ submit: Function }>()
|
||||||
interface Props {
|
const problem = inject<Ref<Problem>>("problem")
|
||||||
state: {
|
|
||||||
language: LANGUAGE
|
|
||||||
code: string
|
|
||||||
}
|
|
||||||
problem: Problem
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Tab {
|
|
||||||
testcase = "testcase",
|
|
||||||
result = "result",
|
|
||||||
}
|
|
||||||
|
|
||||||
const { state, problem } = defineProps<Props>()
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const contestID = <string>route.params.contestID || ""
|
|
||||||
|
|
||||||
const submission = ref<Submission | null>(null)
|
|
||||||
const submissionId = ref("")
|
|
||||||
const tab = ref(Tab.testcase)
|
|
||||||
|
|
||||||
const [submitted] = useToggle()
|
|
||||||
const [tried] = useToggle()
|
const [tried] = useToggle()
|
||||||
|
|
||||||
const { start: submitPending, isPending } = useTimeout(5000, {
|
|
||||||
controls: true,
|
|
||||||
immediate: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { start: fetchSubmission } = useTimeoutFn(
|
|
||||||
async () => {
|
|
||||||
const res = await getSubmission(submissionId.value)
|
|
||||||
submission.value = res.data
|
|
||||||
const result = submission.value.result
|
|
||||||
if (
|
|
||||||
result === SubmissionStatus.judging ||
|
|
||||||
result === SubmissionStatus.pending
|
|
||||||
) {
|
|
||||||
fetchSubmission()
|
|
||||||
} else {
|
|
||||||
submitted.value = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
2000,
|
|
||||||
{ immediate: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkIfTried()
|
checkIfTried()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function checkIfTried() {
|
async function checkIfTried() {
|
||||||
const res = await submissionExists(problem.id)
|
const res = await submissionExists(problem!.value.id)
|
||||||
tried.value = res.data
|
tried.value = res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
const judging = computed(
|
|
||||||
() =>
|
|
||||||
!!(submission.value && submission.value.result === SubmissionStatus.judging)
|
|
||||||
)
|
|
||||||
|
|
||||||
const pending = computed(
|
|
||||||
() =>
|
|
||||||
!!(submission.value && submission.value.result === SubmissionStatus.pending)
|
|
||||||
)
|
|
||||||
|
|
||||||
const submitting = computed(
|
|
||||||
() =>
|
|
||||||
!!(
|
|
||||||
submission.value &&
|
|
||||||
submission.value.result === SubmissionStatus.submitting
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const submitDisabled = computed(() => {
|
|
||||||
const code = state.code
|
|
||||||
if (
|
|
||||||
code.trim() === "" ||
|
|
||||||
code === problem.template[state.language] ||
|
|
||||||
code === SOURCES[state.language]
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (judging.value || pending.value || submitting.value) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (submitted.value) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (isPending.value) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
const submitLabel = computed(() => {
|
|
||||||
if (submitting.value) {
|
|
||||||
return "正在提交"
|
|
||||||
}
|
|
||||||
if (judging.value || pending.value) {
|
|
||||||
return "正在评分"
|
|
||||||
}
|
|
||||||
if (isPending.value) {
|
|
||||||
return "运行结果"
|
|
||||||
}
|
|
||||||
return "点击提交"
|
|
||||||
})
|
|
||||||
|
|
||||||
const msg = computed(() => {
|
|
||||||
if (
|
|
||||||
submission.value &&
|
|
||||||
submission.value.statistic_info &&
|
|
||||||
submission.value.statistic_info.err_info
|
|
||||||
) {
|
|
||||||
return submission.value.statistic_info.err_info
|
|
||||||
}
|
|
||||||
const result = submission.value && submission.value.result
|
|
||||||
if (
|
|
||||||
result === SubmissionStatus.compile_error ||
|
|
||||||
result === SubmissionStatus.runtime_error
|
|
||||||
) {
|
|
||||||
return "请仔细检查,看看代码格式是不是写错了!"
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const infoTable = computed(() => {
|
|
||||||
if (
|
|
||||||
submission.value &&
|
|
||||||
submission.value.result !== SubmissionStatus.accepted &&
|
|
||||||
submission.value.result !== SubmissionStatus.compile_error &&
|
|
||||||
submission.value.result !== SubmissionStatus.runtime_error &&
|
|
||||||
submission.value.info &&
|
|
||||||
submission.value.info.data &&
|
|
||||||
submission.value.info.data.length
|
|
||||||
) {
|
|
||||||
return submission.value.info.data
|
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function submit() {
|
|
||||||
const data: SubmitCodePayload = {
|
|
||||||
problem_id: problem.id,
|
|
||||||
language: state.language,
|
|
||||||
code: state.code,
|
|
||||||
}
|
|
||||||
if (contestID) {
|
|
||||||
data.contest_id = parseInt(contestID)
|
|
||||||
}
|
|
||||||
submission.value = { result: 9 } as Submission
|
|
||||||
const res = await submitCode(data)
|
|
||||||
submissionId.value = res.data.submission_id
|
|
||||||
// 防止重复提交
|
|
||||||
submitPending()
|
|
||||||
submitted.value = true
|
|
||||||
// 查询结果
|
|
||||||
fetchSubmission()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTab(pane: TabsPaneContext) {
|
function onTab(pane: TabsPaneContext) {
|
||||||
if (pane.paneName === Tab.result) {
|
if (pane.paneName === "result") {
|
||||||
submit()
|
submitPanelRef && submitPanelRef.value!.submit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => submission.value && submission.value.result,
|
|
||||||
(result) => {
|
|
||||||
if (result === SubmissionStatus.accepted) {
|
|
||||||
party.confetti(document.body, {
|
|
||||||
count: party.variation.range(200, 400),
|
|
||||||
size: party.variation.skew(2, 0.3),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-tabs type="border-card" @tab-click="onTab" v-model="tab">
|
<el-tabs type="border-card" @tab-click="onTab" v-model="tab">
|
||||||
<el-tab-pane label="测试用例" :name="Tab.testcase">
|
<el-tab-pane label="测试用例" name="testcase">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<el-table height="320"></el-table>
|
<el-table height="320"></el-table>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :disabled="submitDisabled" :name="Tab.result">
|
<SubmitPanel ref="submitPanelRef" />
|
||||||
<template #label>
|
|
||||||
<el-space :size="2">
|
|
||||||
<el-icon>
|
|
||||||
<i-ep-loading v-if="judging || pending || submitting" />
|
|
||||||
<i-ep-bell v-else-if="isPending" />
|
|
||||||
<i-ep-caret-right v-else />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ submitLabel }}</span>
|
|
||||||
</el-space>
|
|
||||||
</template>
|
|
||||||
<div class="panel">
|
|
||||||
<el-alert
|
|
||||||
v-if="submission"
|
|
||||||
:closable="false"
|
|
||||||
:type="JUDGE_STATUS[submission.result]['alertType']"
|
|
||||||
:title="JUDGE_STATUS[submission.result]['name']"
|
|
||||||
>
|
|
||||||
</el-alert>
|
|
||||||
<el-scrollbar
|
|
||||||
v-if="msg || infoTable.length"
|
|
||||||
height="280"
|
|
||||||
class="result"
|
|
||||||
noresize
|
|
||||||
>
|
|
||||||
<div v-if="msg">{{ msg }}</div>
|
|
||||||
<el-table v-if="infoTable.length" :data="infoTable" stripe>
|
|
||||||
<el-table-column prop="test_case" label="测试用例" align="center" />
|
|
||||||
<el-table-column label="测试状态" width="120" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<SubmissionResultTag
|
|
||||||
v-if="scope.row"
|
|
||||||
:result="scope.row.result"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="占用内存" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ submissionMemoryFormat(scope.row.memory) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="执行耗时" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ submissionTimeFormat(scope.row.real_time) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="signal" label="信号" align="center" />
|
|
||||||
</el-table>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
.panel {
|
|
||||||
height: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
margin-top: 12px;
|
|
||||||
white-space: pre;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import loader, { Monaco } from "@monaco-editor/loader"
|
import loader, { Monaco } from "@monaco-editor/loader"
|
||||||
import { ref, onBeforeUnmount, onMounted, watch, reactive } from "vue"
|
import { ref, onBeforeUnmount, onMounted, watch, reactive, provide } from "vue"
|
||||||
import {
|
import {
|
||||||
LANGUAGE_LABEL,
|
LANGUAGE_LABEL,
|
||||||
LANGUAGE_VALUE,
|
LANGUAGE_VALUE,
|
||||||
@@ -10,12 +10,17 @@ import { isMobile } from "../../../utils/breakpoints"
|
|||||||
import { Problem } from "../../../utils/types"
|
import { Problem } from "../../../utils/types"
|
||||||
import EditorExec from "./editor-exec.vue"
|
import EditorExec from "./editor-exec.vue"
|
||||||
|
|
||||||
const { problem } = defineProps<{ problem: Problem }>()
|
interface Props {
|
||||||
const state = reactive({
|
problem: Problem
|
||||||
code: SOURCES[problem.languages[0] || "C"],
|
}
|
||||||
language: problem.languages[0] || "C",
|
|
||||||
isMobile,
|
const props = defineProps<Props>()
|
||||||
|
const code = reactive({
|
||||||
|
value: SOURCES[props.problem.languages[0] || "C"],
|
||||||
|
language: props.problem.languages[0] || "C",
|
||||||
})
|
})
|
||||||
|
provide("code", code)
|
||||||
|
|
||||||
const monacoEditorRef = ref()
|
const monacoEditorRef = ref()
|
||||||
|
|
||||||
let monaco: Monaco
|
let monaco: Monaco
|
||||||
@@ -29,12 +34,12 @@ onBeforeUnmount(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => state.language,
|
() => code.language,
|
||||||
() => {
|
() => {
|
||||||
if (monaco && monaco.editor) {
|
if (monaco && monaco.editor) {
|
||||||
monaco.editor.setModelLanguage(
|
monaco.editor.setModelLanguage(
|
||||||
monaco.editor.getModels()[0],
|
monaco.editor.getModels()[0],
|
||||||
LANGUAGE_VALUE[state.language]
|
LANGUAGE_VALUE[code.language]
|
||||||
)
|
)
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
@@ -42,30 +47,30 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
state.code = problem.template[state.language] || SOURCES[state.language]
|
code.value = props.problem.template[code.language] || SOURCES[code.language]
|
||||||
if (monaco && monaco.editor) {
|
if (monaco && monaco.editor) {
|
||||||
monaco.editor.getModels()[0].setValue(state.code)
|
monaco.editor.getModels()[0].setValue(code.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
state.code = problem.template[state.language] || SOURCES[state.language]
|
code.value = props.problem.template[code.language] || SOURCES[code.language]
|
||||||
monaco = await loader.init()
|
monaco = await loader.init()
|
||||||
monaco.editor.create(monacoEditorRef.value, {
|
monaco.editor.create(monacoEditorRef.value, {
|
||||||
value: state.code, // 编辑器初始显示文字
|
value: code.value, // 编辑器初始显示文字
|
||||||
language: LANGUAGE_VALUE[state.language],
|
language: LANGUAGE_VALUE[code.language],
|
||||||
theme: "vs", // 官方自带三种主题vs, hc-black, or vs-dark
|
theme: "vs-dark", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||||
minimap: {
|
minimap: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
lineNumbersMinChars: 3,
|
lineNumbersMinChars: 3,
|
||||||
automaticLayout: true, // 自适应布局
|
automaticLayout: true, // 自适应布局
|
||||||
tabSize: 4,
|
tabSize: 4,
|
||||||
fontSize: state.isMobile ? 20 : 24, // 字体大小
|
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||||
scrollBeyondLastLine: false, // 取消代码后面一大段空白
|
scrollBeyondLastLine: false, // 取消代码后面一大段空白
|
||||||
})
|
})
|
||||||
monaco.editor.getModels()[0].onDidChangeContent(() => {
|
monaco.editor.getModels()[0].onDidChangeContent(() => {
|
||||||
state.code = monaco.editor.getModels()[0].getValue()
|
code.value = monaco.editor.getModels()[0].getValue()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -73,7 +78,7 @@ async function init() {
|
|||||||
<template>
|
<template>
|
||||||
<el-form inline>
|
<el-form inline>
|
||||||
<el-form-item label="语言" label-width="60">
|
<el-form-item label="语言" label-width="60">
|
||||||
<el-select v-model="state.language" class="language">
|
<el-select v-model="code.language" class="language">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in problem.languages"
|
v-for="item in problem.languages"
|
||||||
:key="item"
|
:key="item"
|
||||||
@@ -91,7 +96,7 @@ async function init() {
|
|||||||
ref="monacoEditorRef"
|
ref="monacoEditorRef"
|
||||||
:class="isMobile ? 'editorMobile' : 'editor'"
|
:class="isMobile ? 'editorMobile' : 'editor'"
|
||||||
></section>
|
></section>
|
||||||
<EditorExec :state="state" :problem="problem" />
|
<EditorExec />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Problem } from "../../../utils/types"
|
import { Problem } from "../../../utils/types"
|
||||||
|
|
||||||
const { problem } = defineProps<{ problem: Problem }>()
|
interface Props {
|
||||||
|
problem: Problem
|
||||||
|
}
|
||||||
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { getACRate, getTagColor, parseTime } from "../../../utils/functions"
|
|||||||
import { isDesktop } from "../../../utils/breakpoints"
|
import { isDesktop } from "../../../utils/breakpoints"
|
||||||
import { Problem } from "../../../utils/types"
|
import { Problem } from "../../../utils/types"
|
||||||
|
|
||||||
const { problem } = defineProps<{ problem: Problem }>()
|
interface Props {
|
||||||
|
problem: Problem
|
||||||
|
}
|
||||||
|
defineProps<Props>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
243
src/oj/problem/components/submit-panel.vue
Normal file
243
src/oj/problem/components/submit-panel.vue
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useTimeout, useTimeoutFn, useToggle } from "@vueuse/core"
|
||||||
|
import { computed, inject, Ref, ref, watch } from "vue"
|
||||||
|
import party from "party-js"
|
||||||
|
import { useRoute } from "vue-router"
|
||||||
|
import {
|
||||||
|
SOURCES,
|
||||||
|
JUDGE_STATUS,
|
||||||
|
SubmissionStatus,
|
||||||
|
} from "../../../utils/constants"
|
||||||
|
import {
|
||||||
|
submissionMemoryFormat,
|
||||||
|
submissionTimeFormat,
|
||||||
|
} from "../../../utils/functions"
|
||||||
|
import {
|
||||||
|
LANGUAGE,
|
||||||
|
Problem,
|
||||||
|
Submission,
|
||||||
|
SubmitCodePayload,
|
||||||
|
} from "../../../utils/types"
|
||||||
|
import { getSubmission, submitCode } from "../../api"
|
||||||
|
import SubmissionResultTag from "../../components/submission-result-tag.vue"
|
||||||
|
const code = inject<{ value: string; language: LANGUAGE }>("code", {
|
||||||
|
value: "",
|
||||||
|
language: "C",
|
||||||
|
})
|
||||||
|
const problem = inject<Ref<Problem>>("problem")
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const contestID = <string>route.params.contestID || ""
|
||||||
|
|
||||||
|
const submissionId = ref("")
|
||||||
|
const submission = ref<Submission | null>(null)
|
||||||
|
|
||||||
|
const [submitted] = useToggle()
|
||||||
|
|
||||||
|
const { start: submitPending, isPending } = useTimeout(5000, {
|
||||||
|
controls: true,
|
||||||
|
immediate: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { start: fetchSubmission } = useTimeoutFn(
|
||||||
|
async () => {
|
||||||
|
const res = await getSubmission(submissionId.value)
|
||||||
|
submission.value = res.data
|
||||||
|
const result = submission.value.result
|
||||||
|
if (
|
||||||
|
result === SubmissionStatus.judging ||
|
||||||
|
result === SubmissionStatus.pending
|
||||||
|
) {
|
||||||
|
fetchSubmission()
|
||||||
|
} else {
|
||||||
|
submitted.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2000,
|
||||||
|
{ immediate: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
const judging = computed(
|
||||||
|
() =>
|
||||||
|
!!(submission.value && submission.value.result === SubmissionStatus.judging)
|
||||||
|
)
|
||||||
|
|
||||||
|
const pending = computed(
|
||||||
|
() =>
|
||||||
|
!!(submission.value && submission.value.result === SubmissionStatus.pending)
|
||||||
|
)
|
||||||
|
|
||||||
|
const submitting = computed(
|
||||||
|
() =>
|
||||||
|
!!(
|
||||||
|
submission.value &&
|
||||||
|
submission.value.result === SubmissionStatus.submitting
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const submitDisabled = computed(() => {
|
||||||
|
const value = code.value
|
||||||
|
if (
|
||||||
|
value.trim() === "" ||
|
||||||
|
value === problem!.value.template[code.language] ||
|
||||||
|
value === SOURCES[code.language]
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (judging.value || pending.value || submitting.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (submitted.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (isPending.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
const submitLabel = computed(() => {
|
||||||
|
if (submitting.value) {
|
||||||
|
return "正在提交"
|
||||||
|
}
|
||||||
|
if (judging.value || pending.value) {
|
||||||
|
return "正在评分"
|
||||||
|
}
|
||||||
|
if (isPending.value) {
|
||||||
|
return "运行结果"
|
||||||
|
}
|
||||||
|
return "点击提交"
|
||||||
|
})
|
||||||
|
|
||||||
|
const msg = computed(() => {
|
||||||
|
if (
|
||||||
|
submission.value &&
|
||||||
|
submission.value.statistic_info &&
|
||||||
|
submission.value.statistic_info.err_info
|
||||||
|
) {
|
||||||
|
return submission.value.statistic_info.err_info
|
||||||
|
}
|
||||||
|
const result = submission.value && submission.value.result
|
||||||
|
if (
|
||||||
|
result === SubmissionStatus.compile_error ||
|
||||||
|
result === SubmissionStatus.runtime_error
|
||||||
|
) {
|
||||||
|
return "请仔细检查,看看代码格式是不是写错了!"
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const infoTable = computed(() => {
|
||||||
|
if (
|
||||||
|
submission.value &&
|
||||||
|
submission.value.result !== SubmissionStatus.accepted &&
|
||||||
|
submission.value.result !== SubmissionStatus.compile_error &&
|
||||||
|
submission.value.result !== SubmissionStatus.runtime_error &&
|
||||||
|
submission.value.info &&
|
||||||
|
submission.value.info.data &&
|
||||||
|
submission.value.info.data.length
|
||||||
|
) {
|
||||||
|
return submission.value.info.data
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
const data: SubmitCodePayload = {
|
||||||
|
problem_id: problem!.value.id,
|
||||||
|
language: code.language,
|
||||||
|
code: code.value,
|
||||||
|
}
|
||||||
|
if (contestID) {
|
||||||
|
data.contest_id = parseInt(contestID)
|
||||||
|
}
|
||||||
|
submission.value = { result: 9 } as Submission
|
||||||
|
const res = await submitCode(data)
|
||||||
|
submissionId.value = res.data.submission_id
|
||||||
|
// 防止重复提交
|
||||||
|
submitPending()
|
||||||
|
submitted.value = true
|
||||||
|
// 查询结果
|
||||||
|
fetchSubmission()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => submission.value && submission.value.result,
|
||||||
|
(result) => {
|
||||||
|
if (result === SubmissionStatus.accepted) {
|
||||||
|
party.confetti(document.body, {
|
||||||
|
count: party.variation.range(200, 400),
|
||||||
|
size: party.variation.skew(2, 0.3),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
defineExpose({ submit })
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<el-tab-pane :disabled="submitDisabled" name="result">
|
||||||
|
<template #label>
|
||||||
|
<el-space :size="2">
|
||||||
|
<el-icon>
|
||||||
|
<i-ep-loading v-if="judging || pending || submitting" />
|
||||||
|
<i-ep-bell v-else-if="isPending" />
|
||||||
|
<i-ep-caret-right v-else />
|
||||||
|
</el-icon>
|
||||||
|
<span>{{ submitLabel }}</span>
|
||||||
|
</el-space>
|
||||||
|
</template>
|
||||||
|
<div class="panel">
|
||||||
|
<el-alert
|
||||||
|
v-if="submission"
|
||||||
|
:closable="false"
|
||||||
|
:type="JUDGE_STATUS[submission.result]['alertType']"
|
||||||
|
:title="JUDGE_STATUS[submission.result]['name']"
|
||||||
|
>
|
||||||
|
</el-alert>
|
||||||
|
<el-scrollbar
|
||||||
|
v-if="msg || infoTable.length"
|
||||||
|
height="280"
|
||||||
|
class="result"
|
||||||
|
noresize
|
||||||
|
>
|
||||||
|
<div v-if="msg">{{ msg }}</div>
|
||||||
|
<el-table v-if="infoTable.length" :data="infoTable" stripe>
|
||||||
|
<el-table-column prop="test_case" label="测试用例" align="center" />
|
||||||
|
<el-table-column label="测试状态" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<SubmissionResultTag
|
||||||
|
v-if="scope.row"
|
||||||
|
:result="scope.row.result"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="占用内存" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ submissionMemoryFormat(scope.row.memory) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="执行耗时" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ submissionTimeFormat(scope.row.real_time) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="signal" label="信号" align="center" />
|
||||||
|
</el-table>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.panel {
|
||||||
|
height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 12px;
|
||||||
|
white-space: pre;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
0
src/oj/problem/components/testcase-panel.vue
Normal file
0
src/oj/problem/components/testcase-panel.vue
Normal file
@@ -4,12 +4,19 @@ import ProblemContent from "./components/problem-content.vue"
|
|||||||
import ProblemInfo from "./components/problem-info.vue"
|
import ProblemInfo from "./components/problem-info.vue"
|
||||||
import { getProblem } from "../api"
|
import { getProblem } from "../api"
|
||||||
import { isDesktop, isMobile } from "../../utils/breakpoints"
|
import { isDesktop, isMobile } from "../../utils/breakpoints"
|
||||||
|
import { provide, readonly } from "vue"
|
||||||
|
|
||||||
const { problemID = "", contestID = "" } = defineProps<{
|
interface Props {
|
||||||
problemID?: string
|
problemID: string
|
||||||
contestID?: string
|
contestID?: string
|
||||||
}>()
|
}
|
||||||
const { data: problem, isFinished } = getProblem(problemID)
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
contestID: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: problem, isFinished } = getProblem(props.problemID)
|
||||||
|
provide("problem", readonly(problem))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -25,7 +32,7 @@ const { data: problem, isFinished } = getProblem(problemID)
|
|||||||
<el-tab-pane v-if="isMobile" label="代码编辑" lazy>
|
<el-tab-pane v-if="isMobile" label="代码编辑" lazy>
|
||||||
<Editor :problem="problem" />
|
<Editor :problem="problem" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="比赛信息" v-if="contestID" lazy></el-tab-pane>
|
<el-tab-pane label="比赛信息" v-if="props.contestID" lazy></el-tab-pane>
|
||||||
<el-tab-pane label="题目信息" lazy>
|
<el-tab-pane label="题目信息" lazy>
|
||||||
<ProblemInfo :problem="problem" />
|
<ProblemInfo :problem="problem" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|||||||
@@ -36,30 +36,32 @@
|
|||||||
import Resizer from "./resizer.vue"
|
import Resizer from "./resizer.vue"
|
||||||
import Pane from "./pane.vue"
|
import Pane from "./pane.vue"
|
||||||
import { computed, ref } from "vue"
|
import { computed, ref } from "vue"
|
||||||
|
import { classNameToArray } from "element-plus/es/utils"
|
||||||
|
|
||||||
const {
|
interface Props {
|
||||||
minPercent = 10,
|
|
||||||
defaultPercent = 50,
|
|
||||||
split,
|
|
||||||
className,
|
|
||||||
} = defineProps<{
|
|
||||||
minPercent?: number
|
minPercent?: number
|
||||||
defaultPercent?: number
|
defaultPercent?: number
|
||||||
split: "vertical" | "horizontal"
|
split: "vertical" | "horizontal"
|
||||||
className?: string
|
className?: string
|
||||||
}>()
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
minPercent: 10,
|
||||||
|
defaultPercent: 50,
|
||||||
|
split: "horizontal",
|
||||||
|
className: "",
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits(["resize"])
|
const emit = defineEmits(["resize"])
|
||||||
|
|
||||||
const active = ref(false)
|
const active = ref(false)
|
||||||
const hasMoved = ref(false)
|
const hasMoved = ref(false)
|
||||||
const percent = ref(defaultPercent)
|
const percent = ref(props.defaultPercent)
|
||||||
const type = ref(split === "vertical" ? "width" : "height")
|
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||||
const resizeType = ref(split === "vertical" ? "left" : "top")
|
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||||
|
|
||||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||||
const cursor = computed(() =>
|
const cursor = computed(() =>
|
||||||
active.value ? (split === "vertical" ? "col-resize" : "row-resize") : ""
|
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// watch(
|
// watch(
|
||||||
@@ -89,7 +91,7 @@ function onMouseMove(e: any) {
|
|||||||
if (active.value) {
|
if (active.value) {
|
||||||
let offset = 0
|
let offset = 0
|
||||||
let target = e.currentTarget
|
let target = e.currentTarget
|
||||||
if (split === "vertical") {
|
if (props.split === "vertical") {
|
||||||
while (target) {
|
while (target) {
|
||||||
offset += target.offsetLeft
|
offset += target.offsetLeft
|
||||||
target = target.offsetParent
|
target = target.offsetParent
|
||||||
@@ -100,14 +102,14 @@ function onMouseMove(e: any) {
|
|||||||
target = target.offsetParent
|
target = target.offsetParent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const currentPage = split === "vertical" ? e.pageX : e.pageY
|
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||||
const targetOffset =
|
const targetOffset =
|
||||||
split === "vertical"
|
props.split === "vertical"
|
||||||
? e.currentTarget.offsetWidth
|
? e.currentTarget.offsetWidth
|
||||||
: e.currentTarget.offsetHeight
|
: e.currentTarget.offsetHeight
|
||||||
const newPercent =
|
const newPercent =
|
||||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||||
if (newPercent > minPercent && newPercent < 100 - minPercent) {
|
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||||
percent.value = newPercent
|
percent.value = newPercent
|
||||||
}
|
}
|
||||||
emit("resize", newPercent)
|
emit("resize", newPercent)
|
||||||
|
|||||||
@@ -5,12 +5,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { className, split } = defineProps<{
|
import { computed } from "vue"
|
||||||
|
|
||||||
|
interface Props {
|
||||||
split: "horizontal" | "vertical"
|
split: "horizontal" | "vertical"
|
||||||
className?: string
|
className?: string
|
||||||
}>()
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
split: "horizontal",
|
||||||
|
className: "",
|
||||||
|
})
|
||||||
|
|
||||||
const classes = $computed(() => [split, className].join(" "))
|
const classes = computed(() => [props.split, props.className].join(" "))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -5,13 +5,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue"
|
import { computed } from "vue"
|
||||||
|
|
||||||
const { className, split } = defineProps<{
|
interface Props {
|
||||||
split: "horizontal" | "vertical"
|
split: "horizontal" | "vertical"
|
||||||
className?: string
|
className?: string
|
||||||
}>()
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
split: "horizontal",
|
||||||
|
className: "",
|
||||||
|
})
|
||||||
|
|
||||||
const classes = computed(() =>
|
const classes = computed(() =>
|
||||||
["splitter-pane-resizer", split, className].join(" ")
|
["splitter-pane-resizer", props.split, props.className].join(" ")
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user