This commit is contained in:
2024-01-23 14:36:21 +08:00
parent d37c124c5a
commit 946ec691af
16 changed files with 198 additions and 45 deletions

View File

@@ -4,7 +4,11 @@ import { useDark } from "@vueuse/core"
import Desktop from "./desktop/index.vue" import Desktop from "./desktop/index.vue"
import Mobile from "./mobile/index.vue" import Mobile from "./mobile/index.vue"
import { isDesktop, isMobile } from "./composables/breakpoints" import { isDesktop, isMobile } from "./composables/breakpoints"
import { onMounted } from "vue"
import { init } from "./composables/code"
const isDark = useDark() const isDark = useDark()
onMounted(init)
</script> </script>
<template> <template>

View File

@@ -24,7 +24,7 @@ function decode(bytes?: string) {
) )
} }
const http = axios.create({ baseURL: `${protocol}://judge0api.xuyue.cc` }) const http = axios.create({ baseURL: `${protocol}://judge0api.hyyz.izhai.net` })
export async function submit(code: Code, input: string) { export async function submit(code: Code, input: string) {
const encodedCode = encode(code.value) const encodedCode = encode(code.value)

View File

@@ -10,8 +10,8 @@ import { oneDark } from "../themes/oneDark"
import { smoothy } from "../themes/smoothy" import { smoothy } from "../themes/smoothy"
interface Props { interface Props {
label: string
modelValue: string modelValue: string
label?: string
language?: LANGUAGE language?: LANGUAGE
fontSize?: number fontSize?: number
readonly?: boolean readonly?: boolean
@@ -56,7 +56,7 @@ function onChange(v: string) {
} }
</script> </script>
<template> <template>
<n-flex align="center" class="header"> <n-flex align="center" class="header" v-if="props.label">
<span class="title">{{ label }}</span> <span class="title">{{ label }}</span>
<slot name="actions"></slot> <slot name="actions"></slot>
</n-flex> </n-flex>
@@ -68,7 +68,7 @@ function onChange(v: string) {
:tabSize="4" :tabSize="4"
:placeholder="props.placeholder" :placeholder="props.placeholder"
:style="{ :style="{
height: 'calc(100% - 60px)', height: props.label ? 'calc(100% - 60px)' : '100%',
fontSize: props.fontSize + 'px', fontSize: props.fontSize + 'px',
}" }"
@change="onChange" @change="onChange"

View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
import { code } from "../composables/code"
import type { SelectOption } from "naive-ui"
const languages: SelectOption[] = [
{ value: "python", label: "Python" },
{ value: "c", label: "C" },
]
</script>
<template>
<n-select
class="select"
placeholder=""
:options="languages"
v-model:value="code.language"
/>
</template>
<style scoped>
.select {
width: 100px;
}
</style>

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
import { useDark, useToggle } from "@vueuse/core"
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>
<template>
<n-button @click="toggleDark()">
{{ isDark ? "浅色" : "深色" }}
</n-button>
</template>

View File

@@ -22,6 +22,8 @@ function findError(line: string, language = "python") {
"NameError: name '(.*?)' is not defined": (name: string) => "NameError: name '(.*?)' is not defined": (name: string) =>
`命名错误,${name} 不知道是什么东西`, `命名错误,${name} 不知道是什么东西`,
"IndentationError: expected an indented block": "缩进错误:这一行需要缩进", "IndentationError: expected an indented block": "缩进错误:这一行需要缩进",
'TypeError: can only concatenate str \\(not "(.*?)"\\) to str':
"文字和数字不能相加",
} }
const c: any = {} const c: any = {}
const regex = { c, python }[language] const regex = { c, python }[language]

View File

@@ -3,13 +3,14 @@ import { Code, LANGUAGE, Cache, Status } from "../types"
import { sources } from "../templates" import { sources } from "../templates"
import { submit } from "../api" import { submit } from "../api"
import { useStorage } from "@vueuse/core" import { useStorage } from "@vueuse/core"
import { isMobile } from "./breakpoints"
const defaultLanguage = "python" const defaultLanguage = "python"
const cache: Cache = { const cache: Cache = {
language: useStorage<LANGUAGE>("code_language", defaultLanguage), language: useStorage<LANGUAGE>("code_language", defaultLanguage),
input: useStorage("code_input", ""), input: useStorage("code_input", ""),
fontsize: useStorage("fontsize", 24), fontsize: useStorage("fontsize", isMobile.value ? 20 : 24),
code: { code: {
python: useStorage("code_python", sources["python"]), python: useStorage("code_python", sources["python"]),
c: useStorage("code_c", sources["c"]), c: useStorage("code_c", sources["c"]),
@@ -24,7 +25,7 @@ export const input = ref(cache.input.value)
export const output = ref("") export const output = ref("")
export const status = ref(Status.NotStarted) export const status = ref(Status.NotStarted)
export const loading = ref(false) export const loading = ref(false)
export const size = ref(24) export const size = ref(cache.fontsize)
watch(size, (value: number) => { watch(size, (value: number) => {
cache.fontsize.value = value cache.fontsize.value = value

4
src/composables/tab.ts Normal file
View File

@@ -0,0 +1,4 @@
import { ref } from "vue"
import { Tab } from "../types"
export const tab = ref<Tab>("code")

View File

@@ -6,18 +6,15 @@ import {
input, input,
output, output,
status, status,
init,
reset, reset,
clearInput, clearInput,
} from "../composables/code" } from "../composables/code"
import { showAnalyse, analyzeError, analyse } from "../composables/analyse" import { showAnalyse, analyzeError, analyse } from "../composables/analyse"
import CodeEditor from "../components/CodeEditor.vue" import CodeEditor from "../components/CodeEditor.vue"
import { computed, onMounted } from "vue" import { computed } from "vue"
import { useMessage } from "naive-ui" import { useMessage } from "naive-ui"
import { Status } from "../types" import { Status } from "../types"
onMounted(init)
const showInputClearBtn = computed(() => !!input.value) const showInputClearBtn = computed(() => !!input.value)
const message = useMessage() const message = useMessage()

View File

@@ -1,16 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { type SelectOption } from "naive-ui" import { size, run, loading } from "../composables/code"
import { useDark, useToggle } from "@vueuse/core" import ThemeButton from "../components/ThemeButton.vue"
import Play from "../icons/Play.vue" import SelectLanguage from "../components/SelectLanguage.vue"
import { code, size, run, loading } from "../composables/code"
const isDark = useDark()
const toggleDark = useToggle(isDark)
const languages: SelectOption[] = [
{ value: "python", label: "Python" },
{ value: "c", label: "C" },
]
</script> </script>
<template> <template>
@@ -18,9 +9,7 @@ const languages: SelectOption[] = [
<n-flex justify="space-between" align="center"> <n-flex justify="space-between" align="center">
<div class="title">徐越的自测猫</div> <div class="title">徐越的自测猫</div>
<n-flex> <n-flex>
<n-button @click="toggleDark()"> <ThemeButton />
{{ isDark ? "浅色" : "深色" }}
</n-button>
<n-input-number <n-input-number
v-model:value="size" v-model:value="size"
class="fontSize" class="fontSize"
@@ -31,18 +20,8 @@ const languages: SelectOption[] = [
> >
<template #prefix>字号</template> <template #prefix>字号</template>
</n-input-number> </n-input-number>
<n-select <SelectLanguage />
class="select" <n-button type="primary" @click="run" :disabled="loading">
placeholder=""
:options="languages"
v-model:value="code.language"
/>
<n-button type="primary" @click="run" :loading="loading">
<template #icon>
<n-icon>
<Play />
</n-icon>
</template>
运行 运行
</n-button> </n-button>
</n-flex> </n-flex>
@@ -54,13 +33,11 @@ const languages: SelectOption[] = [
.header { .header {
height: 60px; height: 60px;
padding: 12px; padding: 12px;
box-sizing: border-box;
} }
.title { .title {
font-size: 20px; font-size: 20px;
} }
.select {
width: 100px;
}
.fontSize { .fontSize {
width: 110px; width: 110px;
} }

View File

@@ -18,10 +18,10 @@ import File from "./File.vue"
import { useMagicKeys, whenever } from "@vueuse/core" import { useMagicKeys, whenever } from "@vueuse/core"
import { ref } from "vue" import { ref } from "vue"
const { alt_shift_p, ctrl_shift_p, ctrl_shift_z } = useMagicKeys()
const show = ref(false) const show = ref(false)
const { alt_shift_p, ctrl_shift_p, ctrl_shift_z } = useMagicKeys()
whenever(alt_shift_p, () => { whenever(alt_shift_p, () => {
show.value = true show.value = true
}) })

View File

@@ -8,7 +8,6 @@ import {
NLayoutContent, NLayoutContent,
NLayoutHeader, NLayoutHeader,
NSelect, NSelect,
NSpace,
NSplit, NSplit,
NFlex, NFlex,
NIcon, NIcon,
@@ -17,6 +16,9 @@ import {
NPopover, NPopover,
NTag, NTag,
NModal, NModal,
NTabs,
NTabPane,
NDropdown,
} from "naive-ui" } from "naive-ui"
import App from "./App.vue" import App from "./App.vue"
import "normalize.css" import "normalize.css"
@@ -29,7 +31,6 @@ const naive = create({
NLayout, NLayout,
NLayoutHeader, NLayoutHeader,
NLayoutContent, NLayoutContent,
NSpace,
NInput, NInput,
NSelect, NSelect,
NSplit, NSplit,
@@ -40,6 +41,9 @@ const naive = create({
NTag, NTag,
NModal, NModal,
NInput, NInput,
NTabs,
NTabPane,
NDropdown,
], ],
}) })

70
src/mobile/Content.vue Normal file
View File

@@ -0,0 +1,70 @@
<script lang="ts" setup>
import CodeEditor from "../components/CodeEditor.vue"
import { code, input, output, size } from "../composables/code"
import { tab } from "../composables/tab"
import { Tab } from "../types"
import ThemeButton from "../components/ThemeButton.vue"
import SelectLanguage from "../components/SelectLanguage.vue"
function onChange(v: Tab) {
tab.value = v
}
function changeSize(v: number) {
if (v > 40 || v < 20) return
size.value = v
}
</script>
<template>
<n-layout-content>
<n-tabs
pane-style="height: calc(100vh - 111px)"
type="segment"
:value="tab"
@update:value="onChange"
>
<n-tab-pane name="code" tab="代码">
<CodeEditor
v-model:model-value="code.value"
:language="code.language"
:font-size="size"
/>
</n-tab-pane>
<n-tab-pane name="input" tab="输入">
<CodeEditor v-model:model-value="input" :font-size="size" />
</n-tab-pane>
<n-tab-pane name="output" tab="输出">
<CodeEditor v-model:model-value="output" readonly :font-size="size" />
</n-tab-pane>
<n-tab-pane name="setting" tab="设置">
<n-flex size="large" vertical class="setting">
<n-flex align="center">
<span>主题</span>
<ThemeButton />
</n-flex>
<n-flex align="center">
<span>语言</span>
<SelectLanguage />
</n-flex>
<n-flex align="center">
<span>字号</span>
<n-flex align="center">
<span :style="{ 'font-size': size + 'px' }">{{ size }}</span>
<n-button @click="changeSize(size - 2)" :disabled="size === 20">
调小
</n-button>
<n-button @click="changeSize(size + 2)" :disabled="size === 40">
调大
</n-button>
</n-flex>
</n-flex>
</n-flex>
</n-tab-pane>
</n-tabs>
</n-layout-content>
</template>
<style scoped>
.setting {
padding: 0 12px;
}
</style>

52
src/mobile/Header.vue Normal file
View File

@@ -0,0 +1,52 @@
<script lang="ts" setup>
import { useMessage, type DropdownOption } from "naive-ui"
import { code, loading, reset, run } from "../composables/code"
import { tab } from "../composables/tab"
import copyTextToClipboard from "copy-text-to-clipboard"
const message = useMessage()
function switchAndRun() {
tab.value = "output"
run()
}
function copy() {
copyTextToClipboard(code.value)
message.success("已经复制好了")
}
const menu: DropdownOption[] = [
{ label: "复制", key: "copy", props: { onClick: copy } },
{ label: "重置", key: "reset", props: { onClick: reset } },
]
</script>
<template>
<n-layout-header class="container" bordered>
<n-flex justify="space-between" align="center">
<div class="title">徐越的自测猫</div>
<n-flex align="center">
<n-dropdown :options="menu">
<n-button size="small">操作</n-button>
</n-dropdown>
<n-button
size="small"
type="primary"
:disabled="loading"
@click="switchAndRun"
>
运行
</n-button>
</n-flex>
</n-flex>
</n-layout-header>
</template>
<style scoped>
.container {
height: 60px;
padding: 12px;
box-sizing: border-box;
}
.title {
font-size: 18px;
}
</style>

View File

@@ -1 +1,8 @@
<template></template> <script lang="ts" setup>
import Content from "./Content.vue"
import Header from "./Header.vue"
</script>
<template>
<Header />
<Content />
</template>

View File

@@ -40,3 +40,5 @@ export interface File {
out: string out: string
error: boolean error: boolean
} }
export type Tab = "code" | "input" | "output" | "setting"