✨ Challenges
This commit is contained in:
30
application/pages/about.vue
Normal file
30
application/pages/about.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="max-w-[720px] mx-auto">
|
||||
<n-card>
|
||||
<brand-header />
|
||||
|
||||
<div>Get everyone interested in programming.</div>
|
||||
|
||||
<n-divider class="mx-[-24px] w-[calc(100%+48px)]" />
|
||||
|
||||
<div class="text-gray text-xs">
|
||||
<div>
|
||||
<nuxt-link class="link" target="_blank" to="https://smartsheep.studio">Made by SmartSheep Studio.</nuxt-link>
|
||||
Proprietary software.
|
||||
</div>
|
||||
<div>Fuxi Development Team © {{ new Date().getFullYear() }}</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NCard, NDivider } from "naive-ui";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.link {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
116
application/pages/challenges/[id].vue
Normal file
116
application/pages/challenges/[id].vue
Normal file
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="md:max-w-[720px] mx-auto">
|
||||
<n-card segmented>
|
||||
<template #header>
|
||||
<n-page-header @back="navigateTo('/')">
|
||||
<template #title>Challenge #{{ challenge?.id }}</template>
|
||||
<template #subtitle>
|
||||
<div class="flex items-center gap-2">
|
||||
<n-tag size="small" class="case-capital">{{ challenge?.status?.replaceAll("-", " ") }}</n-tag>
|
||||
</div>
|
||||
</template>
|
||||
</n-page-header>
|
||||
</template>
|
||||
|
||||
<client-only>
|
||||
<vue-monaco-editor
|
||||
:options="options"
|
||||
v-model:value="answer.code"
|
||||
class="min-h-[360px] code-editor"
|
||||
/>
|
||||
</client-only>
|
||||
|
||||
<n-divider class="mx-[-24px] w-[calc(100%+48px)] divider-below-code" />
|
||||
|
||||
<template #action>
|
||||
<div class="w-full flex justify-between">
|
||||
<div class="flex gap-2">
|
||||
<n-button secondary circle class="rounded-[4px]" type="warning" :disabled="submitting" @click="save">
|
||||
<template #icon>
|
||||
<n-icon :component="Save" />
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<n-button secondary type="error" :disabled="submitting" @click="abandon">放弃</n-button>
|
||||
<n-button type="primary" :disabled="submitting" @click="submit">提交</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NButton, NCard, NDivider, NPageHeader, NTag, NIcon, useDialog, useMessage } from "naive-ui";
|
||||
import { VueMonacoEditor } from "@guolao/vue-monaco-editor";
|
||||
import { Save } from "@vicons/carbon";
|
||||
|
||||
const route = useRoute();
|
||||
const client = useSupabaseClient();
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
|
||||
const options = {
|
||||
minimap: { enabled: false }
|
||||
};
|
||||
|
||||
const submitting = ref(false);
|
||||
|
||||
const { data: challenge } = await client
|
||||
.from("challenges")
|
||||
.select<any, any>("*")
|
||||
.eq("id", route.params.id)
|
||||
.single();
|
||||
|
||||
useHead({
|
||||
title: challenge ? `挑战 #${challenge.id}` : "挑战 #404"
|
||||
});
|
||||
|
||||
const answer = ref(challenge?.answer ?? {});
|
||||
|
||||
async function save() {
|
||||
|
||||
}
|
||||
|
||||
function abandon() {
|
||||
const instance = dialog.warning({
|
||||
title: "警告",
|
||||
content: "你确定要放弃该次挑战?这会让该挑战立刻转化为放弃状态,并且扣除 5 点社会信用点,三思而后行!",
|
||||
positiveText: "确定",
|
||||
negativeText: "再试试",
|
||||
onPositiveClick: async () => {
|
||||
await client
|
||||
.from("challenges")
|
||||
// @ts-ignore
|
||||
.update<any>({ status: "abandoned" })
|
||||
.eq("id", challenge.id);
|
||||
|
||||
const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
|
||||
|
||||
instance.loading = true
|
||||
message.info("已放弃挑战该题");
|
||||
|
||||
await delay(1850);
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.code-editor {
|
||||
min-width: calc(100% + 48px);
|
||||
margin-top: -20px;
|
||||
margin-left: -24px;
|
||||
margin-right: -24px;
|
||||
}
|
||||
|
||||
.divider-below-code {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="md:max-w-[720px] mx-auto">
|
||||
<problems-list />
|
||||
<problem-list />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -31,22 +31,90 @@
|
||||
</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 } from "naive-ui";
|
||||
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 { data: problem } = await client.from("problems").select<any, any>("*").eq("id", route.params.id).single();
|
||||
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>
|
||||
|
Reference in New Issue
Block a user