121 lines
3.4 KiB
Vue
121 lines
3.4 KiB
Vue
<template>
|
|
<div class="md:max-w-[720px] mx-auto">
|
|
<n-card segmented>
|
|
<template #header>
|
|
<n-page-header @back="navigateTo('/')">
|
|
<template #title>{{ problem?.title }}</template>
|
|
<template #subtitle>
|
|
<div class="flex items-center gap-2">
|
|
<n-tag size="small" class="case-capital">{{ problem?.type }}</n-tag>
|
|
</div>
|
|
</template>
|
|
</n-page-header>
|
|
</template>
|
|
|
|
<m-d-c :value="problem?.description" class="problem-description" tag="article" />
|
|
|
|
<n-divider class="mx-[-24px] w-[calc(100%+48px)]" />
|
|
|
|
<section>
|
|
<div v-if="example" class="grid gap-4 -md:gird-cols-1 md:grid-cols-2">
|
|
<div>
|
|
<div>Example Input</div>
|
|
<pre class="language-text" v-if="example?.spec?.stdin">{{ example?.spec?.stdin }}</pre>
|
|
<pre class="language-text font-italic" v-else>本题无输入</pre>
|
|
</div>
|
|
<div>
|
|
<div>Example Output</div>
|
|
<pre class="language-text" v-if="example?.answer?.stdout">{{ example?.answer?.stdout }}</pre>
|
|
<pre class="language-text font-italic" v-else>本题无输出</pre>
|
|
</div>
|
|
</div>
|
|
<n-empty v-else description="本题无公开样例" />
|
|
</section>
|
|
|
|
<template #action>
|
|
<div class="w-full flex justify-end">
|
|
<n-button v-if="!answering" type="primary" :loading="submitting" @click="doChallenge">试答该题</n-button>
|
|
<div v-else>
|
|
<n-button type="primary" disabled>
|
|
<template #icon>
|
|
<n-icon :component="Checkmark" />
|
|
</template>
|
|
正在作答
|
|
</n-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</n-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { NCard, NTag, NPageHeader, NDivider, NEmpty, NButton, NIcon, useMessage } from "naive-ui";
|
|
import { Checkmark } from "@vicons/carbon";
|
|
import "prismjs/themes/prism.css";
|
|
import "prismjs/prism";
|
|
|
|
const user = useSupabaseUser();
|
|
const client = useSupabaseClient();
|
|
const message = useMessage();
|
|
const route = useRoute();
|
|
|
|
const submitting = ref(false);
|
|
const answering = ref(false);
|
|
|
|
const { data: problem } = await client
|
|
.from("problems")
|
|
.select<any, any>("*")
|
|
.eq("id", route.params.id)
|
|
.single();
|
|
const { data: example } = await client
|
|
.from("problem_cases")
|
|
.select<any, any>("*")
|
|
.eq("problem", route.params.id)
|
|
.single();
|
|
|
|
useHead({
|
|
title: problem?.title ?? "未知题目"
|
|
});
|
|
|
|
async function doChallenge() {
|
|
if (problem == null) return;
|
|
|
|
submitting.value = true;
|
|
|
|
let { data } = await client
|
|
.from("challenges")
|
|
.select<any, any>("id")
|
|
.eq("problem", problem?.id)
|
|
.eq("status", "in-progress")
|
|
.single();
|
|
|
|
if (data == null) {
|
|
const res = await client
|
|
.from("challenges")
|
|
// @ts-ignore
|
|
.insert<any>({
|
|
answers: {},
|
|
details: {},
|
|
status: "in-progress",
|
|
problem: problem.id,
|
|
author: user.value?.id
|
|
})
|
|
.select<any, any>()
|
|
.single();
|
|
|
|
if (res.error != null) {
|
|
message.error(`Something went wrong... ${res.error.message}`);
|
|
return;
|
|
} else {
|
|
data = res.data;
|
|
}
|
|
}
|
|
|
|
submitting.value = false;
|
|
answering.value = true;
|
|
navigateTo(`/challenges/${data.id}`, { open: { target: "_blank" } });
|
|
setTimeout(() => answering.value = false, 3000);
|
|
}
|
|
</script>
|