refactor.
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
import { JUDGE_STATUS } from "../../utils/constants"
|
||||
import { SUBMISSION_RESULT } from "../../utils/types"
|
||||
|
||||
const { result } = defineProps<{
|
||||
interface Props {
|
||||
result: SUBMISSION_RESULT
|
||||
}>()
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,279 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import { useTimeout, useTimeoutFn, useToggle } from "@vueuse/core"
|
||||
import { useToggle } from "@vueuse/core"
|
||||
import { TabsPaneContext } from "element-plus"
|
||||
import party from "party-js"
|
||||
import { computed, onMounted, ref, watch } from "vue"
|
||||
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, submissionExists, submitCode } from "../../api"
|
||||
import { inject, onMounted, Ref, ref } from "vue"
|
||||
import { Problem } from "../../../utils/types"
|
||||
import { submissionExists } from "../../api"
|
||||
import SubmitPanel from "./submit-panel.vue"
|
||||
|
||||
import SubmissionResultTag from "../../components/submission-result-tag.vue"
|
||||
|
||||
interface Props {
|
||||
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 tab = ref("testcase")
|
||||
const submitPanelRef = ref<{ submit: Function }>()
|
||||
const problem = inject<Ref<Problem>>("problem")
|
||||
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(() => {
|
||||
checkIfTried()
|
||||
})
|
||||
|
||||
async function checkIfTried() {
|
||||
const res = await submissionExists(problem.id)
|
||||
const res = await submissionExists(problem!.value.id)
|
||||
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) {
|
||||
if (pane.paneName === Tab.result) {
|
||||
submit()
|
||||
if (pane.paneName === "result") {
|
||||
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>
|
||||
|
||||
<template>
|
||||
<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">
|
||||
<el-table height="320"></el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :disabled="submitDisabled" :name="Tab.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>
|
||||
<SubmitPanel ref="submitPanelRef" />
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.panel {
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 12px;
|
||||
white-space: pre;
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
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 {
|
||||
LANGUAGE_LABEL,
|
||||
LANGUAGE_VALUE,
|
||||
@@ -10,12 +10,17 @@ import { isMobile } from "../../../utils/breakpoints"
|
||||
import { Problem } from "../../../utils/types"
|
||||
import EditorExec from "./editor-exec.vue"
|
||||
|
||||
const { problem } = defineProps<{ problem: Problem }>()
|
||||
const state = reactive({
|
||||
code: SOURCES[problem.languages[0] || "C"],
|
||||
language: problem.languages[0] || "C",
|
||||
isMobile,
|
||||
interface Props {
|
||||
problem: Problem
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
let monaco: Monaco
|
||||
@@ -29,12 +34,12 @@ onBeforeUnmount(() => {
|
||||
})
|
||||
|
||||
watch(
|
||||
() => state.language,
|
||||
() => code.language,
|
||||
() => {
|
||||
if (monaco && monaco.editor) {
|
||||
monaco.editor.setModelLanguage(
|
||||
monaco.editor.getModels()[0],
|
||||
LANGUAGE_VALUE[state.language]
|
||||
LANGUAGE_VALUE[code.language]
|
||||
)
|
||||
reset()
|
||||
}
|
||||
@@ -42,30 +47,30 @@ watch(
|
||||
)
|
||||
|
||||
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) {
|
||||
monaco.editor.getModels()[0].setValue(state.code)
|
||||
monaco.editor.getModels()[0].setValue(code.value)
|
||||
}
|
||||
}
|
||||
|
||||
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.editor.create(monacoEditorRef.value, {
|
||||
value: state.code, // 编辑器初始显示文字
|
||||
language: LANGUAGE_VALUE[state.language],
|
||||
theme: "vs", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
value: code.value, // 编辑器初始显示文字
|
||||
language: LANGUAGE_VALUE[code.language],
|
||||
theme: "vs-dark", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true, // 自适应布局
|
||||
tabSize: 4,
|
||||
fontSize: state.isMobile ? 20 : 24, // 字体大小
|
||||
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||
scrollBeyondLastLine: false, // 取消代码后面一大段空白
|
||||
})
|
||||
monaco.editor.getModels()[0].onDidChangeContent(() => {
|
||||
state.code = monaco.editor.getModels()[0].getValue()
|
||||
code.value = monaco.editor.getModels()[0].getValue()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -73,7 +78,7 @@ async function init() {
|
||||
<template>
|
||||
<el-form inline>
|
||||
<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
|
||||
v-for="item in problem.languages"
|
||||
:key="item"
|
||||
@@ -91,7 +96,7 @@ async function init() {
|
||||
ref="monacoEditorRef"
|
||||
:class="isMobile ? 'editorMobile' : 'editor'"
|
||||
></section>
|
||||
<EditorExec :state="state" :problem="problem" />
|
||||
<EditorExec />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { Problem } from "../../../utils/types"
|
||||
|
||||
const { problem } = defineProps<{ problem: Problem }>()
|
||||
interface Props {
|
||||
problem: Problem
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -4,7 +4,10 @@ import { getACRate, getTagColor, parseTime } from "../../../utils/functions"
|
||||
import { isDesktop } from "../../../utils/breakpoints"
|
||||
import { Problem } from "../../../utils/types"
|
||||
|
||||
const { problem } = defineProps<{ problem: Problem }>()
|
||||
interface Props {
|
||||
problem: Problem
|
||||
}
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<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 { getProblem } from "../api"
|
||||
import { isDesktop, isMobile } from "../../utils/breakpoints"
|
||||
import { provide, readonly } from "vue"
|
||||
|
||||
const { problemID = "", contestID = "" } = defineProps<{
|
||||
problemID?: string
|
||||
interface Props {
|
||||
problemID: 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>
|
||||
|
||||
<template>
|
||||
@@ -25,7 +32,7 @@ const { data: problem, isFinished } = getProblem(problemID)
|
||||
<el-tab-pane v-if="isMobile" label="代码编辑" lazy>
|
||||
<Editor :problem="problem" />
|
||||
</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>
|
||||
<ProblemInfo :problem="problem" />
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -36,30 +36,32 @@
|
||||
import Resizer from "./resizer.vue"
|
||||
import Pane from "./pane.vue"
|
||||
import { computed, ref } from "vue"
|
||||
import { classNameToArray } from "element-plus/es/utils"
|
||||
|
||||
const {
|
||||
minPercent = 10,
|
||||
defaultPercent = 50,
|
||||
split,
|
||||
className,
|
||||
} = defineProps<{
|
||||
interface Props {
|
||||
minPercent?: number
|
||||
defaultPercent?: number
|
||||
split: "vertical" | "horizontal"
|
||||
className?: string
|
||||
}>()
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minPercent: 10,
|
||||
defaultPercent: 50,
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits(["resize"])
|
||||
|
||||
const active = ref(false)
|
||||
const hasMoved = ref(false)
|
||||
const percent = ref(defaultPercent)
|
||||
const type = ref(split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(split === "vertical" ? "left" : "top")
|
||||
const percent = ref(props.defaultPercent)
|
||||
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||
|
||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||
const cursor = computed(() =>
|
||||
active.value ? (split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
)
|
||||
|
||||
// watch(
|
||||
@@ -89,7 +91,7 @@ function onMouseMove(e: any) {
|
||||
if (active.value) {
|
||||
let offset = 0
|
||||
let target = e.currentTarget
|
||||
if (split === "vertical") {
|
||||
if (props.split === "vertical") {
|
||||
while (target) {
|
||||
offset += target.offsetLeft
|
||||
target = target.offsetParent
|
||||
@@ -100,14 +102,14 @@ function onMouseMove(e: any) {
|
||||
target = target.offsetParent
|
||||
}
|
||||
}
|
||||
const currentPage = split === "vertical" ? e.pageX : e.pageY
|
||||
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||
const targetOffset =
|
||||
split === "vertical"
|
||||
props.split === "vertical"
|
||||
? e.currentTarget.offsetWidth
|
||||
: e.currentTarget.offsetHeight
|
||||
const newPercent =
|
||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||
if (newPercent > minPercent && newPercent < 100 - minPercent) {
|
||||
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||
percent.value = newPercent
|
||||
}
|
||||
emit("resize", newPercent)
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { className, split } = defineProps<{
|
||||
import { computed } from "vue"
|
||||
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -5,13 +5,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
|
||||
const { className, split } = defineProps<{
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
className?: string
|
||||
}>()
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const classes = computed(() =>
|
||||
["splitter-pane-resizer", split, className].join(" ")
|
||||
["splitter-pane-resizer", props.split, props.className].join(" ")
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user