add editor.
This commit is contained in:
148
src/oj/problem/components/editor.vue
Normal file
148
src/oj/problem/components/editor.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<script lang="ts" setup>
|
||||
import loader, { Monaco } from "@monaco-editor/loader"
|
||||
import { ref, onBeforeUnmount, onMounted, watch, reactive } from "vue"
|
||||
import { onBeforeRouteLeave, useRoute } from "vue-router"
|
||||
import {
|
||||
buildProblemCodeKey,
|
||||
LANGUAGE,
|
||||
languageLabel,
|
||||
languageValue,
|
||||
} from "../../../utils/constants"
|
||||
import storage from "../../../utils/storage"
|
||||
|
||||
const route = useRoute()
|
||||
const { languages, template } = defineProps<{
|
||||
languages: Array<LANGUAGE>
|
||||
template: { [key in LANGUAGE]?: string }
|
||||
}>()
|
||||
|
||||
const state = reactive({
|
||||
value: "",
|
||||
language: languages[0] || "C",
|
||||
theme: "vs",
|
||||
})
|
||||
|
||||
const monacoEditorRef = ref()
|
||||
|
||||
let monaco: Monaco
|
||||
|
||||
function reset() {
|
||||
if (monaco && monaco.editor) {
|
||||
monaco.editor.getModels()[0].setValue(template[state.language] || "")
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initValue()
|
||||
initEditor()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
monaco.editor.getModels().forEach((model) => model.dispose())
|
||||
})
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
const key = buildProblemCodeKey(
|
||||
route.params.problemID as string,
|
||||
route.params.contestID as string
|
||||
)
|
||||
storage.set(key, {
|
||||
code: state.value,
|
||||
language: state.language,
|
||||
theme: state.theme,
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => state.language,
|
||||
() => {
|
||||
if (monaco && monaco.editor) {
|
||||
monaco.editor.setModelLanguage(
|
||||
monaco.editor.getModels()[0],
|
||||
languageValue[state.language]
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function initValue() {
|
||||
const key = buildProblemCodeKey(
|
||||
route.params.problemID as string,
|
||||
route.params.contestID as string
|
||||
)
|
||||
const problemCode = storage.get(key)
|
||||
if (problemCode) {
|
||||
state.value = problemCode.code
|
||||
state.language = problemCode.language
|
||||
state.theme = problemCode.theme
|
||||
}
|
||||
if (!state.value && template[state.language]) {
|
||||
state.value = template[state.language] || ""
|
||||
}
|
||||
}
|
||||
|
||||
async function initEditor() {
|
||||
monaco = await loader.init()
|
||||
monaco.editor.create(monacoEditorRef.value, {
|
||||
value: state.value, // 编辑器初始显示文字
|
||||
language: languageValue[state.language],
|
||||
automaticLayout: true, // 自适应布局
|
||||
theme: state.theme, // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
fontSize: 24, // 字体大小
|
||||
scrollBeyondLastLine: false, // 取消代码后面一大段空白
|
||||
overviewRulerBorder: false, // 不要滚动条的边框
|
||||
})
|
||||
// 监听值的变化
|
||||
monaco.editor.getModels()[0].onDidChangeContent(() => {
|
||||
console.log(1)
|
||||
state.value = monaco.editor.getModels()[0].getValue()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="语言" label-width="60">
|
||||
<el-select v-model="state.language" class="language">
|
||||
<el-option
|
||||
v-for="item in languages"
|
||||
:key="item"
|
||||
:value="item"
|
||||
:label="languageLabel[item]"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div ref="monacoEditorRef" class="editor"></div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="测试用例"> 1 </el-tab-pane>
|
||||
<el-tab-pane label="执行结果"> 2 </el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form class="actions">
|
||||
<el-form-item>
|
||||
<el-button>运行</el-button>
|
||||
<el-button type="primary">提交</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.language {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.editor {
|
||||
height: 500px;
|
||||
}
|
||||
.actions {
|
||||
margin-top: 16px;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
62
src/oj/problem/components/problem-content.vue
Normal file
62
src/oj/problem/components/problem-content.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
const { problem } = defineProps(["problem"])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-scrollbar height="calc(100vh - 171px)" noresize>
|
||||
<h1>{{ problem.title }}</h1>
|
||||
<p class="title">描述</p>
|
||||
<div class="content" v-html="problem.description"></div>
|
||||
|
||||
<p class="title">输入</p>
|
||||
<div class="content" v-html="problem.input_description"></div>
|
||||
|
||||
<p class="title">输出</p>
|
||||
<div class="content" v-html="problem.output_description"></div>
|
||||
|
||||
<div v-if="problem.hint">
|
||||
<p class="title">提示</p>
|
||||
<el-card shadow="never">
|
||||
<div class="content" v-html="problem.hint"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<div v-for="(sample, index) of problem.samples" :key="index">
|
||||
<p class="title">测试用例 {{ index + 1 }}</p>
|
||||
<el-descriptions border direction="vertical">
|
||||
<el-descriptions-item width="50%" label="输入">
|
||||
<div class="testcase">{{ sample.input }}</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item width="50%" label="输出">
|
||||
<div class="testcase">{{ sample.output }}</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="problem.source">
|
||||
<p class="title">来源</p>
|
||||
<div class="content" v-html="problem.source"></div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
font-size: 20px;
|
||||
margin: 24px 0 16px 0;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.content {
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.testcase {
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
51
src/oj/problem/components/problem-info.vue
Normal file
51
src/oj/problem/components/problem-info.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { DIFFICULTY, getTagColor } from "../../../utils/constants"
|
||||
import { getACRate } from "../../../utils/functions"
|
||||
|
||||
const { problem } = defineProps(["problem"])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-descriptions border>
|
||||
<el-descriptions-item label="编号">
|
||||
{{ problem._id }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="出题人">
|
||||
{{ problem.created_by.username }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="难度">
|
||||
<el-tag :type="getTagColor(problem.difficulty)">
|
||||
{{ DIFFICULTY[<"Low" | "Mid" | "High">problem.difficulty] }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="时间限制">
|
||||
{{ problem.time_limit }}毫秒
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="内存限制">
|
||||
{{ problem.memory_limit }}MB
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="类型">
|
||||
{{ problem.rule_type }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="提交正确">
|
||||
{{ problem.accepted_number }}次
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提交错误">
|
||||
{{ problem.submission_number - problem.accepted_number }}次
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="正确率">
|
||||
{{ getACRate(problem.accepted_number, problem.submission_number) }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="标签">
|
||||
<el-space>
|
||||
<el-tag type="info" v-for="tag in problem.tags" :key="tag">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</el-space>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user