update
This commit is contained in:
@@ -34,11 +34,7 @@
|
|||||||
style="width: 120px"
|
style="width: 120px"
|
||||||
:disabled="streaming"
|
:disabled="streaming"
|
||||||
/>
|
/>
|
||||||
<n-button
|
<n-button v-if="streaming" type="error" @click="stopPrompt">
|
||||||
v-if="streaming"
|
|
||||||
type="error"
|
|
||||||
@click="stopPrompt"
|
|
||||||
>
|
|
||||||
停止
|
停止
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
|
|||||||
@@ -1,80 +1,80 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editors-root">
|
<div class="editors-root">
|
||||||
<n-tabs
|
<n-tabs
|
||||||
:value="tab"
|
:value="tab"
|
||||||
:class="`tab-active-${tab}`"
|
:class="`tab-active-${tab}`"
|
||||||
pane-class="pane"
|
pane-class="pane"
|
||||||
style="height: 100%"
|
style="height: 100%"
|
||||||
type="card"
|
type="card"
|
||||||
@update:value="changeTab"
|
@update:value="changeTab"
|
||||||
>
|
>
|
||||||
<n-tab-pane name="html" tab="HTML">
|
<n-tab-pane name="html" tab="HTML">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<n-flex align="center">
|
|
||||||
<Icon :width="20" icon="skill-icons:html"></Icon>
|
|
||||||
<span>HTML</span>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
<Editor v-model:value="html" :font-size="size" language="html" />
|
|
||||||
</n-tab-pane>
|
|
||||||
<n-tab-pane name="css" tab="CSS">
|
|
||||||
<template #tab>
|
|
||||||
<n-flex align="center">
|
|
||||||
<Icon :width="20" icon="skill-icons:css"></Icon>
|
|
||||||
<span>CSS</span>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
<Editor v-model:value="css" :font-size="size" language="css" />
|
|
||||||
</n-tab-pane>
|
|
||||||
<n-tab-pane name="js" tab="JS">
|
|
||||||
<template #tab>
|
|
||||||
<n-flex align="center">
|
|
||||||
<Icon :width="20" icon="skill-icons:javascript"></Icon>
|
|
||||||
<span>JS</span>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
<Editor v-model:value="js" :font-size="size" language="js" />
|
|
||||||
</n-tab-pane>
|
|
||||||
<n-tab-pane name="actions" tab="选项">
|
|
||||||
<template #tab>
|
|
||||||
<n-flex align="center">
|
|
||||||
<Icon :width="20" icon="skill-icons:actix-dark"></Icon>
|
|
||||||
<span>选项</span>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
<n-flex class="wrapper" vertical>
|
|
||||||
<n-flex align="center">
|
|
||||||
<span class="label">重置</span>
|
|
||||||
<n-button @click="reset('html')">HTML</n-button>
|
|
||||||
<n-button @click="reset('css')">CSS</n-button>
|
|
||||||
<n-button @click="reset('js')">JS</n-button>
|
|
||||||
</n-flex>
|
|
||||||
<n-flex align="center">
|
|
||||||
<span class="label">字号</span>
|
|
||||||
<n-flex align="center">
|
<n-flex align="center">
|
||||||
<span :style="{ 'font-size': size + 'px' }">{{ size }}</span>
|
<Icon :width="20" icon="skill-icons:html"></Icon>
|
||||||
<n-button :disabled="size === 20" @click="changeSize(size - 2)">
|
<span>HTML</span>
|
||||||
调小
|
</n-flex>
|
||||||
</n-button>
|
</template>
|
||||||
<n-button :disabled="size === 40" @click="changeSize(size + 2)">
|
<Editor v-model:value="html" :font-size="size" language="html" />
|
||||||
调大
|
</n-tab-pane>
|
||||||
</n-button>
|
<n-tab-pane name="css" tab="CSS">
|
||||||
|
<template #tab>
|
||||||
|
<n-flex align="center">
|
||||||
|
<Icon :width="20" icon="skill-icons:css"></Icon>
|
||||||
|
<span>CSS</span>
|
||||||
|
</n-flex>
|
||||||
|
</template>
|
||||||
|
<Editor v-model:value="css" :font-size="size" language="css" />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="js" tab="JS">
|
||||||
|
<template #tab>
|
||||||
|
<n-flex align="center">
|
||||||
|
<Icon :width="20" icon="skill-icons:javascript"></Icon>
|
||||||
|
<span>JS</span>
|
||||||
|
</n-flex>
|
||||||
|
</template>
|
||||||
|
<Editor v-model:value="js" :font-size="size" language="js" />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="actions" tab="选项">
|
||||||
|
<template #tab>
|
||||||
|
<n-flex align="center">
|
||||||
|
<Icon :width="20" icon="skill-icons:actix-dark"></Icon>
|
||||||
|
<span>选项</span>
|
||||||
|
</n-flex>
|
||||||
|
</template>
|
||||||
|
<n-flex class="wrapper" vertical>
|
||||||
|
<n-flex align="center">
|
||||||
|
<span class="label">重置</span>
|
||||||
|
<n-button @click="reset('html')">HTML</n-button>
|
||||||
|
<n-button @click="reset('css')">CSS</n-button>
|
||||||
|
<n-button @click="reset('js')">JS</n-button>
|
||||||
|
</n-flex>
|
||||||
|
<n-flex align="center">
|
||||||
|
<span class="label">字号</span>
|
||||||
|
<n-flex align="center">
|
||||||
|
<span :style="{ 'font-size': size + 'px' }">{{ size }}</span>
|
||||||
|
<n-button :disabled="size === 20" @click="changeSize(size - 2)">
|
||||||
|
调小
|
||||||
|
</n-button>
|
||||||
|
<n-button :disabled="size === 40" @click="changeSize(size + 2)">
|
||||||
|
调大
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
|
</n-flex>
|
||||||
|
<n-flex align="center">
|
||||||
|
<span class="label">预加载</span>
|
||||||
|
<n-tag type="success">Normalize.css</n-tag>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
<n-flex align="center">
|
</n-tab-pane>
|
||||||
<span class="label">预加载</span>
|
<template #suffix>
|
||||||
<n-tag type="success">Normalize.css</n-tag>
|
<Toolbar
|
||||||
</n-flex>
|
:submit-loading="submitLoading"
|
||||||
</n-flex>
|
@format="format"
|
||||||
</n-tab-pane>
|
@submit="formatAndSubmit"
|
||||||
<template #suffix>
|
/>
|
||||||
<Toolbar
|
</template>
|
||||||
:submit-loading="submitLoading"
|
</n-tabs>
|
||||||
@format="format"
|
|
||||||
@submit="formatAndSubmit"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</n-tabs>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -68,14 +68,31 @@ const emits = defineEmits(["afterScore", "showCode", "clear"])
|
|||||||
|
|
||||||
type Layout = "desktop" | "mobile" | "tablet"
|
type Layout = "desktop" | "mobile" | "tablet"
|
||||||
const layouts: Layout[] = ["desktop", "mobile", "tablet"]
|
const layouts: Layout[] = ["desktop", "mobile", "tablet"]
|
||||||
const layoutConfig: Record<Layout, { icon: string; label: string; width: string }> = {
|
const layoutConfig: Record<
|
||||||
desktop: { icon: "material-symbols:desktop-windows-outline", label: "桌面", width: "100%" },
|
Layout,
|
||||||
mobile: { icon: "material-symbols:smartphone-outline", label: "移动端 (375px)", width: "375px" },
|
{ icon: string; label: string; width: string }
|
||||||
tablet: { icon: "material-symbols:tablet-outline", label: "平板 (768px)", width: "768px" },
|
> = {
|
||||||
|
desktop: {
|
||||||
|
icon: "material-symbols:desktop-windows-outline",
|
||||||
|
label: "桌面",
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
icon: "material-symbols:smartphone-outline",
|
||||||
|
label: "移动端 (375px)",
|
||||||
|
width: "375px",
|
||||||
|
},
|
||||||
|
tablet: {
|
||||||
|
icon: "material-symbols:tablet-outline",
|
||||||
|
label: "平板 (768px)",
|
||||||
|
width: "768px",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const layoutIndex = ref(0)
|
const layoutIndex = ref(0)
|
||||||
const layoutIcon = computed(() => layoutConfig[layouts[layoutIndex.value]].icon)
|
const layoutIcon = computed(() => layoutConfig[layouts[layoutIndex.value]].icon)
|
||||||
const layoutLabel = computed(() => layoutConfig[layouts[layoutIndex.value]].label)
|
const layoutLabel = computed(
|
||||||
|
() => layoutConfig[layouts[layoutIndex.value]].label,
|
||||||
|
)
|
||||||
const iframeWrapperStyle = computed(() => ({
|
const iframeWrapperStyle = computed(() => ({
|
||||||
maxWidth: layoutConfig[layouts[layoutIndex.value]].width,
|
maxWidth: layoutConfig[layouts[layoutIndex.value]].width,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import {
|
|||||||
} from "../../store/user"
|
} from "../../store/user"
|
||||||
import { loginModal } from "../../store/modal"
|
import { loginModal } from "../../store/modal"
|
||||||
import { show, panelSize } from "../../store/panel"
|
import { show, panelSize } from "../../store/panel"
|
||||||
import { step } from "../../store/tutorial"
|
|
||||||
import { taskId } from "../../store/task"
|
import { taskId } from "../../store/task"
|
||||||
import { Account } from "../../api"
|
import { Account } from "../../api"
|
||||||
import { Role } from "../../utils/type"
|
import { Role } from "../../utils/type"
|
||||||
@@ -103,18 +102,9 @@ function showTutorial() {
|
|||||||
function clickMenu(name: string) {
|
function clickMenu(name: string) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "dashboard": {
|
case "dashboard": {
|
||||||
if (roleSuper.value) {
|
const route = roleAdmin.value ? "challenge-editor" : "tutorial-editor"
|
||||||
router.push({
|
router.push({ name: route, query: { display: 0 } })
|
||||||
name: "tutorial-editor",
|
break
|
||||||
params: { display: 0 },
|
|
||||||
})
|
|
||||||
break
|
|
||||||
} else if (roleAdmin.value) {
|
|
||||||
router.push({ name: "challenge-editor", params: { display: 0 } })
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case "admin":
|
case "admin":
|
||||||
window.open(ADMIN_URL)
|
window.open(ADMIN_URL)
|
||||||
|
|||||||
@@ -12,13 +12,17 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<n-flex align="center" :size="6">
|
<n-flex align="center" :size="6">
|
||||||
<span v-if="item.submitted" class="check-icon">✓</span>
|
<span v-if="item.submitted" class="check-icon">✓</span>
|
||||||
<span :class="{ 'submitted-title': item.submitted }">{{ item.title }}</span>
|
<span :class="{ 'submitted-title': item.submitted }">{{
|
||||||
|
item.title
|
||||||
|
}}</span>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<n-flex :size="6">
|
<n-flex :size="6">
|
||||||
<n-tag type="warning" size="small">{{ item.score }} 分</n-tag>
|
<n-tag type="warning" size="small">{{ item.score }} 分</n-tag>
|
||||||
<n-tag v-if="item.pass_score != null" size="small">及格 {{ item.pass_score }} 分</n-tag>
|
<n-tag v-if="item.pass_score != null" size="small"
|
||||||
|
>及格 {{ item.pass_score }} 分</n-tag
|
||||||
|
>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
<n-button text @click="prev()" :disabled="prevDisabled()">
|
<n-button text @click="prev()" :disabled="prevDisabled()">
|
||||||
<Icon :width="24" icon="pepicons-pencil:arrow-left"></Icon>
|
<Icon :width="24" icon="pepicons-pencil:arrow-left"></Icon>
|
||||||
</n-button>
|
</n-button>
|
||||||
<span v-if="progressText" class="progress-text">{{ progressText }}</span>
|
<span v-if="progressText" class="progress-text">{{
|
||||||
|
progressText
|
||||||
|
}}</span>
|
||||||
<n-button text @click="next()" :disabled="nextDisabled()">
|
<n-button text @click="next()" :disabled="nextDisabled()">
|
||||||
<Icon :width="24" icon="pepicons-pencil:arrow-right"></Icon>
|
<Icon :width="24" icon="pepicons-pencil:arrow-right"></Icon>
|
||||||
</n-button>
|
</n-button>
|
||||||
@@ -53,7 +55,14 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Icon } from "@iconify/vue"
|
import { Icon } from "@iconify/vue"
|
||||||
import { computed, watch } from "vue"
|
import { computed, watch } from "vue"
|
||||||
import { step, tutorialIds, prev, next, prevDisabled, nextDisabled } from "../../store/tutorial"
|
import {
|
||||||
|
step,
|
||||||
|
tutorialIds,
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
prevDisabled,
|
||||||
|
nextDisabled,
|
||||||
|
} from "../../store/tutorial"
|
||||||
import { authed, roleSuper } from "../../store/user"
|
import { authed, roleSuper } from "../../store/user"
|
||||||
import { taskTab, taskId, challengeDisplay } from "../../store/task"
|
import { taskTab, taskId, challengeDisplay } from "../../store/task"
|
||||||
import { useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
@@ -71,7 +80,8 @@ function syncRoute(routeName: string) {
|
|||||||
if (route.params.display) step.value = Number(route.params.display)
|
if (route.params.display) step.value = Number(route.params.display)
|
||||||
} else if (routeName.startsWith("home-challenge")) {
|
} else if (routeName.startsWith("home-challenge")) {
|
||||||
taskTab.value = TASK_TYPE.Challenge
|
taskTab.value = TASK_TYPE.Challenge
|
||||||
if (route.params.display) challengeDisplay.value = Number(route.params.display)
|
if (route.params.display)
|
||||||
|
challengeDisplay.value = Number(route.params.display)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syncRoute(route.name as string)
|
syncRoute(route.name as string)
|
||||||
@@ -80,8 +90,7 @@ watch(() => route.name as string, syncRoute)
|
|||||||
defineEmits(["hide"])
|
defineEmits(["hide"])
|
||||||
|
|
||||||
const hideNav = computed(
|
const hideNav = computed(
|
||||||
() =>
|
() => taskTab.value !== TASK_TYPE.Tutorial || tutorialIds.value.length <= 1,
|
||||||
taskTab.value !== TASK_TYPE.Tutorial || tutorialIds.value.length <= 1,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const progressText = computed(() => {
|
const progressText = computed(() => {
|
||||||
|
|||||||
@@ -363,7 +363,8 @@
|
|||||||
width: '20px',
|
width: '20px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
background: i < 3 ? ['#f0a020', '#888', '#a07040'][i] : '#ddd',
|
background:
|
||||||
|
i < 3 ? ['#f0a020', '#888', '#a07040'][i] : '#ddd',
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
fontSize: '11px',
|
fontSize: '11px',
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
@@ -372,7 +373,8 @@
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}"
|
}"
|
||||||
>{{ i + 1 }}</span>
|
>{{ i + 1 }}</span
|
||||||
|
>
|
||||||
<span style="flex: 1; font-size: 13px; color: #333">
|
<span style="flex: 1; font-size: 13px; color: #333">
|
||||||
{{ displayName(item.username, item.classname) }}
|
{{ displayName(item.username, item.classname) }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n-flex style="margin: 0 8px">
|
<n-flex style="margin: 0 8px">
|
||||||
<n-button v-if="roleAdmin || roleSuper" text @click="showStats = true">
|
<n-button
|
||||||
|
v-if="roleAdmin || roleSuper"
|
||||||
|
text
|
||||||
|
@click="showStats = true"
|
||||||
|
>
|
||||||
<Icon :width="16" icon="lucide:bar-chart-2" />
|
<Icon :width="16" icon="lucide:bar-chart-2" />
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-flex class="container" :wrap="false">
|
<n-flex class="container" :wrap="false">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div
|
<div class="back-btn" @click="() => goHome($router, taskTab, step)">
|
||||||
class="back-btn"
|
|
||||||
@click="() => goHome($router, taskTab, step)"
|
|
||||||
>
|
|
||||||
← 返回
|
← 返回
|
||||||
</div>
|
</div>
|
||||||
<n-divider style="margin: 8px 0" />
|
<n-divider style="margin: 8px 0" />
|
||||||
@@ -78,7 +75,9 @@ const menu = computed(() =>
|
|||||||
color: #888;
|
color: #888;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
transition: background-color 0.15s, color 0.15s;
|
transition:
|
||||||
|
background-color 0.15s,
|
||||||
|
color 0.15s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +92,9 @@ const menu = computed(() =>
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #444;
|
color: #444;
|
||||||
transition: background-color 0.15s, color 0.15s;
|
transition:
|
||||||
|
background-color 0.15s,
|
||||||
|
color 0.15s;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,17 @@
|
|||||||
style="height: 100%; padding-right: 10px; overflow: hidden"
|
style="height: 100%; padding-right: 10px; overflow: hidden"
|
||||||
>
|
>
|
||||||
<n-flex justify="space-between" style="flex-shrink: 0">
|
<n-flex justify="space-between" style="flex-shrink: 0">
|
||||||
<n-button secondary @click="() => goHome($router, taskTab, taskTab === TASK_TYPE.Challenge ? challengeDisplay : step)">
|
<n-button
|
||||||
|
secondary
|
||||||
|
@click="
|
||||||
|
() =>
|
||||||
|
goHome(
|
||||||
|
$router,
|
||||||
|
taskTab,
|
||||||
|
taskTab === TASK_TYPE.Challenge ? challengeDisplay : step,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
首页
|
首页
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-flex align="center">
|
<n-flex align="center">
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-split
|
<n-split :size="panelSize" @update-size="changeSize" min="400px" max="900px">
|
||||||
:size="panelSize"
|
|
||||||
@update-size="changeSize"
|
|
||||||
min="400px"
|
|
||||||
max="900px"
|
|
||||||
>
|
|
||||||
<template #1>
|
<template #1>
|
||||||
<TaskPanel @hide="hide" />
|
<TaskPanel @hide="hide" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user