refactor editor and panel.

This commit is contained in:
2023-01-31 22:16:41 +08:00
parent 7b0163885a
commit 17aa4afc04
8 changed files with 115 additions and 74 deletions

4
src/components.d.ts vendored
View File

@@ -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']

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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"

View File

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

View File

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