add submissions.
This commit is contained in:
@@ -1,98 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import type * as Monaco from "monaco-editor"
|
||||
import { LANGUAGE_VALUE } from "utils/constants"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { isMobile } from "~/shared/composables/breakpoints"
|
||||
import { isDark } from "../composables/dark"
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
language?: LANGUAGE
|
||||
height?: string
|
||||
fontSize?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
language: "C",
|
||||
height: "calc(100vh - 100px)",
|
||||
fontSize: 20,
|
||||
class: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "change", value: string): void
|
||||
}>()
|
||||
|
||||
const monacoEditorRef = ref()
|
||||
let editor: Monaco.editor.IStandaloneCodeEditor
|
||||
|
||||
onMounted(function () {
|
||||
const model = window.monaco.editor.createModel(
|
||||
props.value,
|
||||
LANGUAGE_VALUE[props.language]
|
||||
)
|
||||
|
||||
editor = window.monaco.editor.create(monacoEditorRef.value, {
|
||||
model,
|
||||
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true, // 自适应布局
|
||||
tabSize: 4,
|
||||
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||
scrollBeyondLastLine: false,
|
||||
lineDecorationsWidth: 0,
|
||||
scrollBeyondLastColumn: 0,
|
||||
glyphMargin: false,
|
||||
scrollbar: {
|
||||
useShadows: false,
|
||||
vertical: "hidden",
|
||||
horizontal: "hidden",
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
})
|
||||
|
||||
model.onDidChangeContent(() => {
|
||||
const value = model.getValue().toString()
|
||||
emit("change", value)
|
||||
})
|
||||
|
||||
editor.onKeyDown((e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
|
||||
e.preventDefault()
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.value !== model.getValue()) {
|
||||
model.setValue(props.value)
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
editor && editor.dispose()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
ref="monacoEditorRef"
|
||||
:class="props.class"
|
||||
:style="{ height: props.height }"
|
||||
></div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import type * as Monaco from "monaco-editor"
|
||||
import { LANGUAGE_VALUE } from "utils/constants"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { isMobile } from "~/shared/composables/breakpoints"
|
||||
import { isDark } from "../composables/dark"
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
language?: LANGUAGE
|
||||
height?: string
|
||||
fontSize?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
language: "C",
|
||||
height: "calc(100vh - 100px)",
|
||||
fontSize: 20,
|
||||
class: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "change", value: string): void
|
||||
}>()
|
||||
|
||||
const monacoEditorRef = ref()
|
||||
let editor: Monaco.editor.IStandaloneCodeEditor
|
||||
|
||||
onMounted(function () {
|
||||
const model = window.monaco.editor.createModel(
|
||||
props.value,
|
||||
LANGUAGE_VALUE[props.language]
|
||||
)
|
||||
|
||||
editor = window.monaco.editor.create(monacoEditorRef.value, {
|
||||
model,
|
||||
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true, // 自适应布局
|
||||
tabSize: 4,
|
||||
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||
scrollBeyondLastLine: false,
|
||||
lineDecorationsWidth: 0,
|
||||
scrollBeyondLastColumn: 0,
|
||||
glyphMargin: false,
|
||||
scrollbar: {
|
||||
useShadows: false,
|
||||
vertical: "hidden",
|
||||
horizontal: "hidden",
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
})
|
||||
|
||||
model.onDidChangeContent(() => {
|
||||
const value = model.getValue().toString()
|
||||
emit("change", value)
|
||||
})
|
||||
|
||||
editor.onKeyDown((e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
|
||||
e.preventDefault()
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.value !== model.getValue()) {
|
||||
model.setValue(props.value)
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
editor && editor.dispose()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
ref="monacoEditorRef"
|
||||
:class="props.class"
|
||||
:style="{ height: props.height }"
|
||||
></div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||
interface Props {
|
||||
total: number
|
||||
limit: number
|
||||
page: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
limit: 10,
|
||||
page: 1,
|
||||
})
|
||||
|
||||
const emit = defineEmits(["update:limit", "update:page"])
|
||||
|
||||
const limit = ref(props.limit)
|
||||
const page = ref(props.page)
|
||||
|
||||
watch(limit, () => emit("update:limit", limit))
|
||||
watch(page, () => emit("update:page", page))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-pagination
|
||||
class="right margin"
|
||||
:layout="isDesktop ? 'prev,pager,next,sizes' : 'prev,next,sizes'"
|
||||
background
|
||||
:total="props.total"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:pager-count="5"
|
||||
v-model:page-size="limit"
|
||||
v-model:current-page="page"
|
||||
/>
|
||||
</template>
|
||||
<style scoped>
|
||||
.margin {
|
||||
margin-top: 24px;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||
interface Props {
|
||||
total: number
|
||||
limit: number
|
||||
page: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
limit: 10,
|
||||
page: 1,
|
||||
})
|
||||
|
||||
const emit = defineEmits(["update:limit", "update:page"])
|
||||
|
||||
const limit = ref(props.limit)
|
||||
const page = ref(props.page)
|
||||
|
||||
watch(limit, () => emit("update:limit", limit))
|
||||
watch(page, () => emit("update:page", page))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-pagination
|
||||
class="right margin"
|
||||
:layout="isDesktop ? 'prev,pager,next,sizes' : 'prev,next,sizes'"
|
||||
background
|
||||
:total="props.total"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:pager-count="5"
|
||||
v-model:page-size="limit"
|
||||
v-model:current-page="page"
|
||||
/>
|
||||
</template>
|
||||
<style scoped>
|
||||
.margin {
|
||||
margin-top: 24px;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const classes = computed(() => [props.split, props.className].join(" "))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splitter-pane.vertical.splitter-paneL {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.splitter-pane.vertical.splitter-paneR {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
height: 100%;
|
||||
padding-left: 3px;
|
||||
}
|
||||
.splitter-pane.horizontal.splitter-paneL {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter-pane.horizontal.splitter-paneR {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
padding-top: 3px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const classes = computed(() => [props.split, props.className].join(" "))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splitter-pane.vertical.splitter-paneL {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.splitter-pane.vertical.splitter-paneR {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
height: 100%;
|
||||
padding-left: 3px;
|
||||
}
|
||||
.splitter-pane.horizontal.splitter-paneL {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter-pane.horizontal.splitter-paneR {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
padding-top: 3px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<template>
|
||||
<div :class="classes"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const classes = computed(() =>
|
||||
["splitter-pane-resizer", props.split, props.className].join(" ")
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splitter-pane-resizer {
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
position: absolute;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.splitter-pane-resizer.horizontal {
|
||||
height: 11px;
|
||||
margin: -5px 0;
|
||||
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: row-resize;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter-pane-resizer.vertical {
|
||||
width: 11px;
|
||||
height: 100%;
|
||||
margin-left: -5px;
|
||||
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: col-resize;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div :class="classes"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
|
||||
interface Props {
|
||||
split: "horizontal" | "vertical"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const classes = computed(() =>
|
||||
["splitter-pane-resizer", props.split, props.className].join(" ")
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splitter-pane-resizer {
|
||||
box-sizing: border-box;
|
||||
background: #000;
|
||||
position: absolute;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.splitter-pane-resizer.horizontal {
|
||||
height: 11px;
|
||||
margin: -5px 0;
|
||||
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: row-resize;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter-pane-resizer.vertical {
|
||||
width: 11px;
|
||||
height: 100%;
|
||||
margin-left: -5px;
|
||||
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: col-resize;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{ cursor, userSelect }"
|
||||
class="vue-splitter-container clearfix"
|
||||
@mouseup="onMouseUp"
|
||||
@mousemove="onMouseMove"
|
||||
>
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneL"
|
||||
:split="split"
|
||||
:style="{ [type]: percent + '%' }"
|
||||
>
|
||||
<slot name="panel"></slot>
|
||||
</Pane>
|
||||
|
||||
<Resizer
|
||||
:className="className"
|
||||
:style="{ [resizeType]: percent + '%' }"
|
||||
:split="split"
|
||||
@mousedown.native="onMouseDown"
|
||||
@click.native="onClick"
|
||||
></Resizer>
|
||||
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneR"
|
||||
:split="split"
|
||||
:style="{ [type]: 100 - percent + '%' }"
|
||||
>
|
||||
<slot name="paner"></slot>
|
||||
</Pane>
|
||||
<div class="vue-splitter-container-mask" v-if="active"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Resizer from "./Resizer.vue"
|
||||
import Pane from "./Pane.vue"
|
||||
import { computed, ref } from "vue"
|
||||
|
||||
interface Props {
|
||||
minPercent?: number
|
||||
defaultPercent?: number
|
||||
split: "vertical" | "horizontal"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minPercent: 10,
|
||||
defaultPercent: 50,
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits(["resize"])
|
||||
|
||||
const active = ref(false)
|
||||
const hasMoved = ref(false)
|
||||
const percent = ref(props.defaultPercent)
|
||||
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||
|
||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||
const cursor = computed(() =>
|
||||
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
)
|
||||
|
||||
// watch(
|
||||
// () => defaultPercent,
|
||||
// (newValue) => {
|
||||
// percent.value = newValue
|
||||
// }
|
||||
// )
|
||||
|
||||
function onClick() {
|
||||
if (!hasMoved.value) {
|
||||
percent.value = 50
|
||||
emit("resize", percent.value)
|
||||
}
|
||||
}
|
||||
function onMouseDown() {
|
||||
active.value = true
|
||||
hasMoved.value = false
|
||||
}
|
||||
function onMouseUp() {
|
||||
active.value = false
|
||||
}
|
||||
function onMouseMove(e: any) {
|
||||
if (e.buttons === 0) {
|
||||
active.value = false
|
||||
}
|
||||
if (active.value) {
|
||||
let offset = 0
|
||||
let target = e.currentTarget
|
||||
if (props.split === "vertical") {
|
||||
while (target) {
|
||||
offset += target.offsetLeft
|
||||
target = target.offsetParent
|
||||
}
|
||||
} else {
|
||||
while (target) {
|
||||
offset += target.offsetTop
|
||||
target = target.offsetParent
|
||||
}
|
||||
}
|
||||
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||
const targetOffset =
|
||||
props.split === "vertical"
|
||||
? e.currentTarget.offsetWidth
|
||||
: e.currentTarget.offsetHeight
|
||||
const newPercent =
|
||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||
percent.value = newPercent
|
||||
}
|
||||
emit("resize", newPercent)
|
||||
hasMoved.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
.vue-splitter-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.vue-splitter-container-mask {
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div
|
||||
:style="{ cursor, userSelect }"
|
||||
class="vue-splitter-container clearfix"
|
||||
@mouseup="onMouseUp"
|
||||
@mousemove="onMouseMove"
|
||||
>
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneL"
|
||||
:split="split"
|
||||
:style="{ [type]: percent + '%' }"
|
||||
>
|
||||
<slot name="panel"></slot>
|
||||
</Pane>
|
||||
|
||||
<Resizer
|
||||
:className="className"
|
||||
:style="{ [resizeType]: percent + '%' }"
|
||||
:split="split"
|
||||
@mousedown.native="onMouseDown"
|
||||
@click.native="onClick"
|
||||
></Resizer>
|
||||
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneR"
|
||||
:split="split"
|
||||
:style="{ [type]: 100 - percent + '%' }"
|
||||
>
|
||||
<slot name="paner"></slot>
|
||||
</Pane>
|
||||
<div class="vue-splitter-container-mask" v-if="active"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Resizer from "./Resizer.vue"
|
||||
import Pane from "./Pane.vue"
|
||||
import { computed, ref } from "vue"
|
||||
|
||||
interface Props {
|
||||
minPercent?: number
|
||||
defaultPercent?: number
|
||||
split: "vertical" | "horizontal"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minPercent: 10,
|
||||
defaultPercent: 50,
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits(["resize"])
|
||||
|
||||
const active = ref(false)
|
||||
const hasMoved = ref(false)
|
||||
const percent = ref(props.defaultPercent)
|
||||
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||
|
||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||
const cursor = computed(() =>
|
||||
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
)
|
||||
|
||||
// watch(
|
||||
// () => defaultPercent,
|
||||
// (newValue) => {
|
||||
// percent.value = newValue
|
||||
// }
|
||||
// )
|
||||
|
||||
function onClick() {
|
||||
if (!hasMoved.value) {
|
||||
percent.value = 50
|
||||
emit("resize", percent.value)
|
||||
}
|
||||
}
|
||||
function onMouseDown() {
|
||||
active.value = true
|
||||
hasMoved.value = false
|
||||
}
|
||||
function onMouseUp() {
|
||||
active.value = false
|
||||
}
|
||||
function onMouseMove(e: any) {
|
||||
if (e.buttons === 0) {
|
||||
active.value = false
|
||||
}
|
||||
if (active.value) {
|
||||
let offset = 0
|
||||
let target = e.currentTarget
|
||||
if (props.split === "vertical") {
|
||||
while (target) {
|
||||
offset += target.offsetLeft
|
||||
target = target.offsetParent
|
||||
}
|
||||
} else {
|
||||
while (target) {
|
||||
offset += target.offsetTop
|
||||
target = target.offsetParent
|
||||
}
|
||||
}
|
||||
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||
const targetOffset =
|
||||
props.split === "vertical"
|
||||
? e.currentTarget.offsetWidth
|
||||
: e.currentTarget.offsetHeight
|
||||
const newPercent =
|
||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||
percent.value = newPercent
|
||||
}
|
||||
emit("resize", newPercent)
|
||||
hasMoved.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
.vue-splitter-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.vue-splitter-container-mask {
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
export const isMobile = breakpoints.smallerOrEqual("md")
|
||||
export const isDesktop = breakpoints.greater("md")
|
||||
import { breakpointsTailwind } from "@vueuse/core"
|
||||
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||
|
||||
export const isMobile = breakpoints.smallerOrEqual("md")
|
||||
export const isDesktop = breakpoints.greater("md")
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export const isDark = useDark({ storageKey: "theme-appearance" })
|
||||
export const toggleDark = useToggle(isDark)
|
||||
export const isDark = useDark({ storageKey: "theme-appearance" })
|
||||
export const toggleDark = useToggle(isDark)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export const [loginModal, toggleLogin] = useToggle()
|
||||
export const [signupModal, toggleSignup] = useToggle()
|
||||
export const [loginModal, toggleLogin] = useToggle()
|
||||
export const [signupModal, toggleSignup] = useToggle()
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<router-view></router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<router-view></router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import Login from "../Login/index.vue"
|
||||
import Signup from "../Signup/index.vue"
|
||||
import Header from "../Header/index.vue"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header class="header">
|
||||
<Header />
|
||||
</el-header>
|
||||
<el-main>
|
||||
<router-view></router-view>
|
||||
</el-main>
|
||||
<Login />
|
||||
<Signup />
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import Login from "../Login/index.vue"
|
||||
import Signup from "../Signup/index.vue"
|
||||
import Header from "../Header/index.vue"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header class="header">
|
||||
<Header />
|
||||
</el-header>
|
||||
<el-main>
|
||||
<router-view></router-view>
|
||||
</el-main>
|
||||
<Login />
|
||||
<Signup />
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,98 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import type * as Monaco from "monaco-editor"
|
||||
import { LANGUAGE_VALUE } from "utils/constants"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { isMobile } from "~/shared/composables/breakpoints"
|
||||
import { isDark } from "../composables/dark"
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
language?: LANGUAGE
|
||||
height?: string
|
||||
fontSize?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
language: "C",
|
||||
height: "calc(100vh - 100px)",
|
||||
fontSize: 20,
|
||||
class: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "change", value: string): void
|
||||
}>()
|
||||
|
||||
const monacoEditorRef = ref()
|
||||
let editor: Monaco.editor.IStandaloneCodeEditor
|
||||
|
||||
onMounted(function () {
|
||||
const model = window.monaco.editor.createModel(
|
||||
props.value,
|
||||
LANGUAGE_VALUE[props.language]
|
||||
)
|
||||
|
||||
editor = window.monaco.editor.create(monacoEditorRef.value, {
|
||||
model,
|
||||
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true, // 自适应布局
|
||||
tabSize: 4,
|
||||
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||
scrollBeyondLastLine: false,
|
||||
lineDecorationsWidth: 0,
|
||||
scrollBeyondLastColumn: 0,
|
||||
glyphMargin: false,
|
||||
scrollbar: {
|
||||
useShadows: false,
|
||||
vertical: "hidden",
|
||||
horizontal: "hidden",
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
})
|
||||
|
||||
model.onDidChangeContent(() => {
|
||||
const value = model.getValue().toString()
|
||||
emit("change", value)
|
||||
})
|
||||
|
||||
editor.onKeyDown((e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
|
||||
e.preventDefault()
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.value !== model.getValue()) {
|
||||
model.setValue(props.value)
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
editor && editor.dispose()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
ref="monacoEditorRef"
|
||||
:class="props.class"
|
||||
:style="{ height: props.height }"
|
||||
></div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import type * as Monaco from "monaco-editor"
|
||||
import { LANGUAGE_VALUE } from "utils/constants"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { isMobile } from "~/shared/composables/breakpoints"
|
||||
import { isDark } from "../composables/dark"
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
language?: LANGUAGE
|
||||
height?: string
|
||||
fontSize?: number
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
language: "C",
|
||||
height: "calc(100vh - 100px)",
|
||||
fontSize: 20,
|
||||
class: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "change", value: string): void
|
||||
}>()
|
||||
|
||||
const monacoEditorRef = ref()
|
||||
let editor: Monaco.editor.IStandaloneCodeEditor
|
||||
|
||||
onMounted(function () {
|
||||
const model = window.monaco.editor.createModel(
|
||||
props.value,
|
||||
LANGUAGE_VALUE[props.language]
|
||||
)
|
||||
|
||||
editor = window.monaco.editor.create(monacoEditorRef.value, {
|
||||
model,
|
||||
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true, // 自适应布局
|
||||
tabSize: 4,
|
||||
fontSize: isMobile.value ? 20 : 24, // 字体大小
|
||||
scrollBeyondLastLine: false,
|
||||
lineDecorationsWidth: 0,
|
||||
scrollBeyondLastColumn: 0,
|
||||
glyphMargin: false,
|
||||
scrollbar: {
|
||||
useShadows: false,
|
||||
vertical: "hidden",
|
||||
horizontal: "hidden",
|
||||
},
|
||||
overviewRulerLanes: 0,
|
||||
})
|
||||
|
||||
model.onDidChangeContent(() => {
|
||||
const value = model.getValue().toString()
|
||||
emit("change", value)
|
||||
})
|
||||
|
||||
editor.onKeyDown((e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
|
||||
e.preventDefault()
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.value !== model.getValue()) {
|
||||
model.setValue(props.value)
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
editor && editor.dispose()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
ref="monacoEditorRef"
|
||||
:class="props.class"
|
||||
:style="{ height: props.height }"
|
||||
></div>
|
||||
</template>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{ cursor, userSelect }"
|
||||
class="vue-splitter-container clearfix"
|
||||
@mouseup="onMouseUp"
|
||||
@mousemove="onMouseMove"
|
||||
>
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneL"
|
||||
:split="split"
|
||||
:style="{ [type]: percent + '%' }"
|
||||
>
|
||||
<slot name="panel"></slot>
|
||||
</Pane>
|
||||
|
||||
<Resizer
|
||||
:className="className"
|
||||
:style="{ [resizeType]: percent + '%' }"
|
||||
:split="split"
|
||||
@mousedown.native="onMouseDown"
|
||||
@click.native="onClick"
|
||||
></Resizer>
|
||||
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneR"
|
||||
:split="split"
|
||||
:style="{ [type]: 100 - percent + '%' }"
|
||||
>
|
||||
<slot name="paner"></slot>
|
||||
</Pane>
|
||||
<div class="vue-splitter-container-mask" v-if="active"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Resizer from "./Resizer.vue"
|
||||
import Pane from "./Pane.vue"
|
||||
import { computed, ref } from "vue"
|
||||
|
||||
interface Props {
|
||||
minPercent?: number
|
||||
defaultPercent?: number
|
||||
split: "vertical" | "horizontal"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minPercent: 10,
|
||||
defaultPercent: 50,
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits(["resize"])
|
||||
|
||||
const active = ref(false)
|
||||
const hasMoved = ref(false)
|
||||
const percent = ref(props.defaultPercent)
|
||||
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||
|
||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||
const cursor = computed(() =>
|
||||
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
)
|
||||
|
||||
// watch(
|
||||
// () => defaultPercent,
|
||||
// (newValue) => {
|
||||
// percent.value = newValue
|
||||
// }
|
||||
// )
|
||||
|
||||
function onClick() {
|
||||
if (!hasMoved.value) {
|
||||
percent.value = 50
|
||||
emit("resize", percent.value)
|
||||
}
|
||||
}
|
||||
function onMouseDown() {
|
||||
active.value = true
|
||||
hasMoved.value = false
|
||||
}
|
||||
function onMouseUp() {
|
||||
active.value = false
|
||||
}
|
||||
function onMouseMove(e: any) {
|
||||
if (e.buttons === 0) {
|
||||
active.value = false
|
||||
}
|
||||
if (active.value) {
|
||||
let offset = 0
|
||||
let target = e.currentTarget
|
||||
if (props.split === "vertical") {
|
||||
while (target) {
|
||||
offset += target.offsetLeft
|
||||
target = target.offsetParent
|
||||
}
|
||||
} else {
|
||||
while (target) {
|
||||
offset += target.offsetTop
|
||||
target = target.offsetParent
|
||||
}
|
||||
}
|
||||
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||
const targetOffset =
|
||||
props.split === "vertical"
|
||||
? e.currentTarget.offsetWidth
|
||||
: e.currentTarget.offsetHeight
|
||||
const newPercent =
|
||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||
percent.value = newPercent
|
||||
}
|
||||
emit("resize", newPercent)
|
||||
hasMoved.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
.vue-splitter-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.vue-splitter-container-mask {
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div
|
||||
:style="{ cursor, userSelect }"
|
||||
class="vue-splitter-container clearfix"
|
||||
@mouseup="onMouseUp"
|
||||
@mousemove="onMouseMove"
|
||||
>
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneL"
|
||||
:split="split"
|
||||
:style="{ [type]: percent + '%' }"
|
||||
>
|
||||
<slot name="panel"></slot>
|
||||
</Pane>
|
||||
|
||||
<Resizer
|
||||
:className="className"
|
||||
:style="{ [resizeType]: percent + '%' }"
|
||||
:split="split"
|
||||
@mousedown.native="onMouseDown"
|
||||
@click.native="onClick"
|
||||
></Resizer>
|
||||
|
||||
<Pane
|
||||
class="splitter-pane splitter-paneR"
|
||||
:split="split"
|
||||
:style="{ [type]: 100 - percent + '%' }"
|
||||
>
|
||||
<slot name="paner"></slot>
|
||||
</Pane>
|
||||
<div class="vue-splitter-container-mask" v-if="active"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Resizer from "./Resizer.vue"
|
||||
import Pane from "./Pane.vue"
|
||||
import { computed, ref } from "vue"
|
||||
|
||||
interface Props {
|
||||
minPercent?: number
|
||||
defaultPercent?: number
|
||||
split: "vertical" | "horizontal"
|
||||
className?: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minPercent: 10,
|
||||
defaultPercent: 50,
|
||||
split: "horizontal",
|
||||
className: "",
|
||||
})
|
||||
|
||||
const emit = defineEmits(["resize"])
|
||||
|
||||
const active = ref(false)
|
||||
const hasMoved = ref(false)
|
||||
const percent = ref(props.defaultPercent)
|
||||
const type = ref(props.split === "vertical" ? "width" : "height")
|
||||
const resizeType = ref(props.split === "vertical" ? "left" : "top")
|
||||
|
||||
const userSelect = computed(() => (active.value ? "none" : "auto"))
|
||||
const cursor = computed(() =>
|
||||
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
|
||||
)
|
||||
|
||||
// watch(
|
||||
// () => defaultPercent,
|
||||
// (newValue) => {
|
||||
// percent.value = newValue
|
||||
// }
|
||||
// )
|
||||
|
||||
function onClick() {
|
||||
if (!hasMoved.value) {
|
||||
percent.value = 50
|
||||
emit("resize", percent.value)
|
||||
}
|
||||
}
|
||||
function onMouseDown() {
|
||||
active.value = true
|
||||
hasMoved.value = false
|
||||
}
|
||||
function onMouseUp() {
|
||||
active.value = false
|
||||
}
|
||||
function onMouseMove(e: any) {
|
||||
if (e.buttons === 0) {
|
||||
active.value = false
|
||||
}
|
||||
if (active.value) {
|
||||
let offset = 0
|
||||
let target = e.currentTarget
|
||||
if (props.split === "vertical") {
|
||||
while (target) {
|
||||
offset += target.offsetLeft
|
||||
target = target.offsetParent
|
||||
}
|
||||
} else {
|
||||
while (target) {
|
||||
offset += target.offsetTop
|
||||
target = target.offsetParent
|
||||
}
|
||||
}
|
||||
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
|
||||
const targetOffset =
|
||||
props.split === "vertical"
|
||||
? e.currentTarget.offsetWidth
|
||||
: e.currentTarget.offsetHeight
|
||||
const newPercent =
|
||||
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
|
||||
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
|
||||
percent.value = newPercent
|
||||
}
|
||||
emit("resize", newPercent)
|
||||
hasMoved.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
.vue-splitter-container {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.vue-splitter-container-mask {
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user