refactor editor and panel.
This commit is contained in:
4
src/components.d.ts
vendored
4
src/components.d.ts
vendored
@@ -11,6 +11,9 @@ declare module '@vue/runtime-core' {
|
|||||||
IEpCaretRight: typeof import('~icons/ep/caret-right')['default']
|
IEpCaretRight: typeof import('~icons/ep/caret-right')['default']
|
||||||
IEpLoading: typeof import('~icons/ep/loading')['default']
|
IEpLoading: typeof import('~icons/ep/loading')['default']
|
||||||
IEpLock: typeof import('~icons/ep/lock')['default']
|
IEpLock: typeof import('~icons/ep/lock')['default']
|
||||||
|
IEpMenu: typeof import('~icons/ep/menu')['default']
|
||||||
|
IEpMore: typeof import('~icons/ep/more')['default']
|
||||||
|
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
||||||
NAlert: typeof import('naive-ui')['NAlert']
|
NAlert: typeof import('naive-ui')['NAlert']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
||||||
@@ -36,6 +39,7 @@ declare module '@vue/runtime-core' {
|
|||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
NPagination: typeof import('naive-ui')['NPagination']
|
NPagination: typeof import('naive-ui')['NPagination']
|
||||||
|
NPopover: typeof import('naive-ui')['NPopover']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ import { SOURCES } from "utils/constants"
|
|||||||
import { Problem } from "utils/types"
|
import { Problem } from "utils/types"
|
||||||
import Monaco from "~/shared/Monaco.vue"
|
import Monaco from "~/shared/Monaco.vue"
|
||||||
import { code } from "oj/composables/code"
|
import { code } from "oj/composables/code"
|
||||||
|
import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
|
||||||
|
import { DropdownOption } from "naive-ui"
|
||||||
|
|
||||||
|
const Submit = defineAsyncComponent(() => import("./Submit.vue"))
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
problem: Problem
|
problem: Problem
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
code.language = props.problem.languages[0] || "C"
|
code.language = props.problem.languages[0] || "C"
|
||||||
code.value = props.problem.template[code.language] || SOURCES[code.language]
|
code.value = props.problem.template[code.language] || SOURCES[code.language]
|
||||||
@@ -22,8 +27,10 @@ function reset() {
|
|||||||
function change(value: string) {
|
function change(value: string) {
|
||||||
code.value = value
|
code.value = value
|
||||||
}
|
}
|
||||||
|
function goSubmissions() {
|
||||||
const options = props.problem.languages.map((it) => ({
|
router.push(`/submission?problem=${props.problem._id}`)
|
||||||
|
}
|
||||||
|
const options: DropdownOption[] = props.problem.languages.map((it) => ({
|
||||||
label: () => [
|
label: () => [
|
||||||
h("img", {
|
h("img", {
|
||||||
src: `/${it}.svg`,
|
src: `/${it}.svg`,
|
||||||
@@ -38,11 +45,27 @@ const options = props.problem.languages.map((it) => ({
|
|||||||
],
|
],
|
||||||
value: it,
|
value: it,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const menu: DropdownOption[] = [
|
||||||
|
{ label: "重置", key: "reset" },
|
||||||
|
{ label: "提交信息", key: "submissions" },
|
||||||
|
]
|
||||||
|
|
||||||
|
function select(key: string) {
|
||||||
|
switch (key) {
|
||||||
|
case "reset":
|
||||||
|
reset()
|
||||||
|
break
|
||||||
|
case "submissions":
|
||||||
|
goSubmissions()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-form inline label-placement="left">
|
<n-form inline label-placement="left">
|
||||||
<n-form-item label="语言">
|
<n-form-item :label="isDesktop ? '语言' : ''">
|
||||||
<n-select
|
<n-select
|
||||||
class="language"
|
class="language"
|
||||||
v-model:value="code.language"
|
v-model:value="code.language"
|
||||||
@@ -50,11 +73,26 @@ const options = props.problem.languages.map((it) => ({
|
|||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
|
<Submit />
|
||||||
|
</n-form-item>
|
||||||
|
<n-dropdown
|
||||||
|
v-if="isMobile"
|
||||||
|
trigger="click"
|
||||||
|
:options="menu"
|
||||||
|
@select="select"
|
||||||
|
>
|
||||||
|
<n-button>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<i-ep-more-filled />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
<n-form-item v-if="isDesktop">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button @click="reset">重置</n-button>
|
<n-button @click="reset">重置</n-button>
|
||||||
<n-button @click="$router.push(`/submission?problem=${problem._id}`)">
|
<n-button @click="goSubmissions">提交信息</n-button>
|
||||||
提交信息
|
|
||||||
</n-button>
|
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
@@ -63,7 +101,7 @@ const options = props.problem.languages.map((it) => ({
|
|||||||
:language="code.language"
|
:language="code.language"
|
||||||
:value="code.value"
|
:value="code.value"
|
||||||
@change="change"
|
@change="change"
|
||||||
height="calc(100vh - 594px)"
|
height="calc(100vh - 200px)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { createTestSubmission } from "utils/judge"
|
|
||||||
import { code } from "oj/composables/code"
|
import { code } from "oj/composables/code"
|
||||||
import party from "party-js"
|
import party from "party-js"
|
||||||
import { Ref } from "vue"
|
import { Ref } from "vue"
|
||||||
@@ -7,7 +6,7 @@ import { SOURCES, JUDGE_STATUS, SubmissionStatus } from "utils/constants"
|
|||||||
import { submissionMemoryFormat, submissionTimeFormat } from "utils/functions"
|
import { submissionMemoryFormat, submissionTimeFormat } from "utils/functions"
|
||||||
import { Problem, Submission, SubmitCodePayload } from "utils/types"
|
import { Problem, Submission, SubmitCodePayload } from "utils/types"
|
||||||
import { getSubmission, submitCode } from "oj/api"
|
import { getSubmission, submitCode } from "oj/api"
|
||||||
import SubmissionResultTag from "../../../shared/SubmissionResultTag.vue"
|
import SubmissionResultTag from "~/shared/SubmissionResultTag.vue"
|
||||||
import type { DataTableColumn } from "naive-ui"
|
import type { DataTableColumn } from "naive-ui"
|
||||||
|
|
||||||
const problem = inject<Ref<Problem>>("problem")
|
const problem = inject<Ref<Problem>>("problem")
|
||||||
@@ -16,9 +15,7 @@ const route = useRoute()
|
|||||||
const contestID = <string>route.params.contestID ?? ""
|
const contestID = <string>route.params.contestID ?? ""
|
||||||
|
|
||||||
const submissionId = ref("")
|
const submissionId = ref("")
|
||||||
const submission = ref<Submission | null>(null)
|
const submission = ref<Submission>()
|
||||||
const input = ref("")
|
|
||||||
const result = ref("")
|
|
||||||
const [submitted] = useToggle()
|
const [submitted] = useToggle()
|
||||||
|
|
||||||
const { start: submitPending, isPending } = useTimeout(5000, {
|
const { start: submitPending, isPending } = useTimeout(5000, {
|
||||||
@@ -186,69 +183,51 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
async function testcaseSubmit() {
|
|
||||||
const res = await createTestSubmission(code, input.value)
|
|
||||||
result.value = res.output
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabProps = {
|
|
||||||
onClick() {
|
|
||||||
if (!submitDisabled.value) {
|
|
||||||
submit()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-tabs default-value="testcase">
|
<n-popover
|
||||||
<n-tab-pane name="testcase" tab="测试面板">
|
trigger="click"
|
||||||
<n-scrollbar style="height: 400px">
|
placement="bottom-end"
|
||||||
<n-form inline label-placement="left">
|
scrollable
|
||||||
<n-form-item label="输入">
|
:show-arrow="false"
|
||||||
<n-input type="textarea" v-model:value="input" />
|
style="max-height: 600px"
|
||||||
</n-form-item>
|
>
|
||||||
<n-form-item>
|
<template #trigger>
|
||||||
<n-button @click="testcaseSubmit">运行</n-button>
|
<n-button type="primary" :disabled="submitDisabled" @click="submit">
|
||||||
</n-form-item>
|
<template #icon>
|
||||||
</n-form>
|
<n-icon>
|
||||||
<div class="msg">{{ result }}</div>
|
<i-ep-loading v-if="judging || pending || submitting" />
|
||||||
</n-scrollbar>
|
<i-ep-bell v-else-if="isPending" />
|
||||||
</n-tab-pane>
|
<i-ep-caret-right v-else />
|
||||||
<n-tab-pane name="submit" :disabled="submitDisabled" :tab-props="tabProps">
|
</n-icon>
|
||||||
<template #tab>
|
</template>
|
||||||
<n-icon>
|
{{ submitLabel }}
|
||||||
<i-ep-loading v-if="judging || pending || submitting" />
|
</n-button>
|
||||||
<i-ep-bell v-else-if="isPending" />
|
</template>
|
||||||
<i-ep-caret-right v-else />
|
<template #header>
|
||||||
</n-icon>
|
<n-alert
|
||||||
<span>{{ submitLabel }}</span>
|
v-if="submission"
|
||||||
</template>
|
:type="JUDGE_STATUS[submission.result]['type']"
|
||||||
<n-scrollbar style="height: 400px">
|
:title="JUDGE_STATUS[submission.result]['name']"
|
||||||
<n-alert
|
/>
|
||||||
v-if="submission"
|
</template>
|
||||||
:type="JUDGE_STATUS[submission.result]['type']"
|
<n-space vertical v-if="msg || infoTable.length">
|
||||||
:title="JUDGE_STATUS[submission.result]['name']"
|
<n-card v-if="msg" embedded class="msg">{{ msg }}</n-card>
|
||||||
/>
|
<n-data-table
|
||||||
<div v-if="msg" class="msg result">{{ msg }}</div>
|
v-if="infoTable.length"
|
||||||
<n-data-table
|
size="small"
|
||||||
v-if="infoTable.length"
|
striped
|
||||||
size="small"
|
:data="infoTable"
|
||||||
striped
|
:columns="columns"
|
||||||
:data="infoTable"
|
/>
|
||||||
:columns="columns"
|
</n-space>
|
||||||
/>
|
</n-popover>
|
||||||
</n-scrollbar>
|
|
||||||
</n-tab-pane>
|
|
||||||
</n-tabs>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.msg {
|
.msg {
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
word-break: break-all;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
.result {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -4,7 +4,6 @@ import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
|
|||||||
import { Problem } from "utils/types"
|
import { Problem } from "utils/types"
|
||||||
|
|
||||||
const Editor = defineAsyncComponent(() => import("./components/Editor.vue"))
|
const Editor = defineAsyncComponent(() => import("./components/Editor.vue"))
|
||||||
const Panel = defineAsyncComponent(() => import("./components/Panel.vue"))
|
|
||||||
const ProblemContent = defineAsyncComponent(
|
const ProblemContent = defineAsyncComponent(
|
||||||
() => import("./components/ProblemContent.vue")
|
() => import("./components/ProblemContent.vue")
|
||||||
)
|
)
|
||||||
@@ -42,7 +41,6 @@ provide("problem", readonly(problem))
|
|||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane v-if="isMobile" name="editor" tab="代码编辑">
|
<n-tab-pane v-if="isMobile" name="editor" tab="代码编辑">
|
||||||
<Editor :problem="problem" />
|
<Editor :problem="problem" />
|
||||||
<Panel />
|
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane
|
<n-tab-pane
|
||||||
name="contest"
|
name="contest"
|
||||||
@@ -56,7 +54,6 @@ provide("problem", readonly(problem))
|
|||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi v-if="isDesktop">
|
<n-gi v-if="isDesktop">
|
||||||
<Editor :problem="problem" />
|
<Editor :problem="problem" />
|
||||||
<Panel />
|
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export const useContestStore = defineStore("contest", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function _getProblems(contestID: string) {
|
async function _getProblems(contestID: string) {
|
||||||
|
problems.value = []
|
||||||
try {
|
try {
|
||||||
problems.value = await getContestProblem(contestID)
|
problems.value = await getContestProblem(contestID)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -16,12 +16,20 @@ const props = defineProps<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const submission = ref<Submission>()
|
const submission = ref<Submission>()
|
||||||
|
const [copied, toggle] = useToggle()
|
||||||
|
const { start } = useTimeoutFn(() => toggle(false), 1000, { immediate: false })
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const res = await getSubmission(props.submissionID)
|
const res = await getSubmission(props.submissionID)
|
||||||
submission.value = res.data
|
submission.value = res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCopy(v: string) {
|
||||||
|
copy(v)
|
||||||
|
toggle(true)
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
|
||||||
const columns: DataTableColumn<Submission["info"]["data"][number]>[] = [
|
const columns: DataTableColumn<Submission["info"]["data"][number]>[] = [
|
||||||
{ title: "测试用例", key: "test_case" },
|
{ title: "测试用例", key: "test_case" },
|
||||||
{
|
{
|
||||||
@@ -59,7 +67,9 @@ onMounted(init)
|
|||||||
</n-alert>
|
</n-alert>
|
||||||
<n-card embedded>
|
<n-card embedded>
|
||||||
<n-space justify="end">
|
<n-space justify="end">
|
||||||
<n-button @click="copy(submission!.code)">复制代码</n-button>
|
<n-button @click="handleCopy(submission!.code)">
|
||||||
|
{{ copied ? "已复制" : "复制代码" }}
|
||||||
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<n-code
|
<n-code
|
||||||
class="code"
|
class="code"
|
||||||
|
|||||||
@@ -15,10 +15,16 @@ function handleClick(value: string) {
|
|||||||
<template>
|
<template>
|
||||||
<n-tooltip trigger="hover">
|
<n-tooltip trigger="hover">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-icon @click="handleClick(value)">
|
<n-icon class="icon" @click="handleClick(value)">
|
||||||
<component :is="copied ? Select : DocumentCopy"></component>
|
<component :is="copied ? Select : DocumentCopy"></component>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
{{ copied ? "已复制" : "复制" }}
|
{{ copied ? "已复制" : "复制" }}
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.icon {
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translateY(2px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -107,7 +107,13 @@ const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
|
|||||||
<n-button @click="toggleSignup(true)">注册</n-button>
|
<n-button @click="toggleSignup(true)">注册</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<n-dropdown :options="menus">
|
<n-dropdown :options="menus">
|
||||||
<n-button>菜单</n-button>
|
<n-button>
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<i-ep-menu />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user