add editor.

This commit is contained in:
2023-01-06 20:09:26 +08:00
parent 2c7da88b06
commit c3746ad105
14 changed files with 298 additions and 169 deletions

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

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

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