教程的标题

This commit is contained in:
2025-10-06 19:13:01 +08:00
parent bc13976a78
commit 96adf39cba
5 changed files with 444 additions and 478 deletions

View File

@@ -1,331 +1,323 @@
<script setup lang="ts"> <script setup lang="ts">
import { NButton, NCheckbox, NSelect, NTag } from "naive-ui" import { NButton, NCheckbox, NSelect, NTag } from "naive-ui"
import { parseTime } from "utils/functions" import { parseTime } from "utils/functions"
import { getACMHelperList, getContest, updateACMHelperChecked } from "../api" import { getACMHelperList, getContest, updateACMHelperChecked } from "../api"
import { getSubmission, getSubmissions } from "oj/api" import { getSubmission, getSubmissions } from "oj/api"
import SubmissionDetail from "oj/submission/detail.vue" import SubmissionDetail from "oj/submission/detail.vue"
import { isDesktop } from "shared/composables/breakpoints" import { isDesktop } from "shared/composables/breakpoints"
interface Props { interface Props {
contestID: string contestID: string
} }
interface HelperItem { interface HelperItem {
id: number id: number
username: string username: string
real_name: string real_name: string
problem_id: string problem_id: string
problem_display_id: string problem_display_id: string
ac_info: { ac_info: {
is_ac: boolean is_ac: boolean
ac_time: number ac_time: number
error_number: number error_number: number
checked?: boolean checked?: boolean
} }
checked: boolean checked: boolean
} }
const props = defineProps<Props>() const props = defineProps<Props>()
const message = useMessage() const message = useMessage()
const submissions = ref<HelperItem[]>([]) const submissions = ref<HelperItem[]>([])
const contestStartTime = ref<Date | null>(null) const contestStartTime = ref<Date | null>(null)
const query = reactive({ const query = reactive({
username: "", username: "",
problemId: "", problemId: "",
checked: "all", checked: "all",
}) })
// 检查状态选项 // 检查状态选项
const checkedOptions = [ const checkedOptions = [
{ label: "全部", value: "all" }, { label: "全部", value: "all" },
{ label: "已检查", value: "checked" }, { label: "已检查", value: "checked" },
{ label: "未检查", value: "unchecked" }, { label: "未检查", value: "unchecked" },
] ]
// 代码查看模态框 // 代码查看模态框
const [codePanel, toggleCodePanel] = useToggle(false) const [codePanel, toggleCodePanel] = useToggle(false)
const currentSubmission = ref<any>(null) const currentSubmission = ref<any>(null)
// 格式化 AC 时间ac_time 是相对于比赛开始的秒数) // 格式化 AC 时间ac_time 是相对于比赛开始的秒数)
function formatACTime(relativeSeconds: number) { function formatACTime(relativeSeconds: number) {
if (!contestStartTime.value) return "-" if (!contestStartTime.value) return "-"
const acTime = new Date( const acTime = new Date(
contestStartTime.value.getTime() + relativeSeconds * 1000, contestStartTime.value.getTime() + relativeSeconds * 1000,
) )
return parseTime(acTime, "YYYY-MM-DD HH:mm:ss") return parseTime(acTime, "YYYY-MM-DD HH:mm:ss")
} }
// 切换检查状态 // 切换检查状态
async function toggleChecked(item: HelperItem) { async function toggleChecked(item: HelperItem) {
const newChecked = !item.checked const newChecked = !item.checked
try { try {
await updateACMHelperChecked( await updateACMHelperChecked(
Number(props.contestID), Number(props.contestID),
item.id, item.id,
item.problem_id, item.problem_id,
newChecked, newChecked,
) )
// 更新本地状态 // 更新本地状态
item.checked = newChecked item.checked = newChecked
item.ac_info.checked = newChecked item.ac_info.checked = newChecked
// 强制触发响应式更新 // 强制触发响应式更新
submissions.value = [...submissions.value] submissions.value = [...submissions.value]
message.success(newChecked ? "已标记为已检查" : "已取消标记") message.success(newChecked ? "已标记为已检查" : "已取消标记")
} catch (err: any) { } catch (err: any) {
message.error(err.data || "操作失败") message.error(err.data || "操作失败")
} }
} }
// 批量标记为已检查 // 批量标记为已检查
async function markAllAsChecked() { async function markAllAsChecked() {
const unchecked = filteredSubmissions.value.filter((item) => !item.checked) const unchecked = filteredSubmissions.value.filter((item) => !item.checked)
if (unchecked.length === 0) { if (unchecked.length === 0) {
message.info("没有需要标记的提交") message.info("没有需要标记的提交")
return return
} }
const loadingMsg = message.loading("正在标记...", { duration: 0 }) const loadingMsg = message.loading("正在标记...", { duration: 0 })
try { try {
for (const item of unchecked) { for (const item of unchecked) {
await updateACMHelperChecked( await updateACMHelperChecked(
Number(props.contestID), Number(props.contestID),
item.id, item.id,
item.problem_id, item.problem_id,
true, true,
) )
item.checked = true item.checked = true
item.ac_info.checked = true item.ac_info.checked = true
} }
// 强制触发响应式更新 // 强制触发响应式更新
submissions.value = [...submissions.value] submissions.value = [...submissions.value]
loadingMsg.destroy() loadingMsg.destroy()
message.success(`已标记 ${unchecked.length} 个提交为已检查`) message.success(`已标记 ${unchecked.length} 个提交为已检查`)
} catch (err: any) { } catch (err: any) {
loadingMsg.destroy() loadingMsg.destroy()
message.error(err.data || "批量操作失败") message.error(err.data || "批量操作失败")
} }
} }
// 过滤后的提交列表 // 过滤后的提交列表
const filteredSubmissions = computed(() => { const filteredSubmissions = computed(() => {
return submissions.value.filter((item) => { return submissions.value.filter((item) => {
if (query.username && !item.username.includes(query.username)) if (query.username && !item.username.includes(query.username)) return false
return false if (query.problemId && !item.problem_display_id.includes(query.problemId))
if (query.problemId && !item.problem_display_id.includes(query.problemId)) return false
return false if (query.checked === "checked" && !item.checked) return false
if (query.checked === "checked" && !item.checked) if (query.checked === "unchecked" && item.checked) return false
return false return true
if (query.checked === "unchecked" && item.checked) })
return false })
return true
}) // 统计信息
}) const stats = computed(() => {
const total = submissions.value.length
// 统计信息 const checked = submissions.value.filter((item) => item.checked).length
const stats = computed(() => { const unchecked = total - checked
const total = submissions.value.length return { total, checked, unchecked }
const checked = submissions.value.filter((item) => item.checked).length })
const unchecked = total - checked
return { total, checked, unchecked } // 查看代码 - 获取该用户在该题目的 AC 提交
}) async function viewSubmission(item: HelperItem) {
try {
// 查看代码 - 获取该用户在该题目的 AC 提交 // 查该用户在该竞赛该题目的 AC 提交
async function viewSubmission(item: HelperItem) { const res = await getSubmissions({
try { username: item.username,
// 查询该用户在该竞赛该题目的 AC 提交 problem_id: item.problem_display_id,
const res = await getSubmissions({ contest_id: props.contestID,
username: item.username, result: "0", // ACCEPTED
problem_id: item.problem_display_id, language: "",
contest_id: props.contestID, page: 1,
result: "0", // ACCEPTED offset: 0,
language: "", limit: 1,
page: 1, })
offset: 0,
limit: 1, if (res.data.results.length === 0) {
}) message.warning("未找到该用户的 AC 提交")
return
if (res.data.results.length === 0) { }
message.warning("未找到该用户的 AC 提交")
return // 获取提交详情
} const submissionListItem = res.data.results[0]
const detailRes = await getSubmission(submissionListItem.id)
// 获取提交详情
const submissionListItem = res.data.results[0] // 手动添加 contest 字段ACM模式下后端不返回此字段
const detailRes = await getSubmission(submissionListItem.id) currentSubmission.value = {
...detailRes.data,
// 手动添加 contest 字段ACM模式下后端不返回此字段 contest: Number(props.contestID),
currentSubmission.value = { problem_display_id: item.problem_display_id,
...detailRes.data, }
contest: Number(props.contestID),
problem_display_id: item.problem_display_id, toggleCodePanel(true)
} } catch (err: any) {
message.error(err.data || "加载提交失败")
toggleCodePanel(true) }
} catch (err: any) { }
message.error(err.data || "加载提交失败")
} // 加载数据
} async function loadData() {
try {
// 加载数据 // 先获取比赛信息,获取开始时间
async function loadData() { const contestRes = await getContest(props.contestID)
try { contestStartTime.value = new Date(contestRes.data.start_time)
// 先获取比赛信息,获取开始时间
const contestRes = await getContest(props.contestID) // 再获取 AC 提交列表
contestStartTime.value = new Date(contestRes.data.start_time) const { data } = await getACMHelperList(Number(props.contestID))
submissions.value = data
// 再获取 AC 提交列表 } catch (err: any) {
const { data } = await getACMHelperList(Number(props.contestID)) message.error(err.data || "加载失败")
submissions.value = data }
} catch (err: any) { }
message.error(err.data || "加载失败")
} const columns: DataTableColumn<HelperItem>[] = [
} {
title: "用户名",
const columns: DataTableColumn<HelperItem>[] = [ key: "username",
{ width: 150,
title: "用户名", },
key: "username", {
width: 150, title: "题目",
}, key: "problem_display_id",
{ width: 100,
title: "题目", render: (row) => h(NTag, { type: "info" }, () => row.problem_display_id),
key: "problem_display_id", },
width: 100, {
render: (row) => h(NTag, { type: "info" }, () => row.problem_display_id), title: "AC时间",
}, key: "ac_time",
{ width: 180,
title: "AC时间", render: (row) => formatACTime(row.ac_info.ac_time),
key: "ac_time", },
width: 180, {
render: (row) => formatACTime(row.ac_info.ac_time), title: "错误次数",
}, key: "error_number",
{ width: 100,
title: "错误次数", render: (row) =>
key: "error_number", h(
width: 100, NTag,
render: (row) => {
h( type: row.ac_info.error_number > 0 ? "warning" : "success",
NTag, size: "small",
{ },
type: row.ac_info.error_number > 0 ? "warning" : "success", () => row.ac_info.error_number,
size: "small", ),
}, },
() => row.ac_info.error_number, {
), title: "已检查",
}, key: "checked",
{ width: 100,
title: "已检查", render: (row) =>
key: "checked", h(NCheckbox, {
width: 100, checked: row.checked,
render: (row) => onUpdateChecked: () => toggleChecked(row),
h(NCheckbox, { }),
checked: row.checked, },
onUpdateChecked: () => toggleChecked(row), {
}), title: "操作",
}, key: "actions",
{ width: 100,
title: "操作", render: (row) =>
key: "actions", h(
width: 100, NButton,
render: (row) => {
h( size: "small",
NButton, type: "primary",
{ secondary: true,
size: "small", onClick: () => viewSubmission(row),
type: "primary", },
secondary: true, () => "查看代码",
onClick: () => viewSubmission(row), ),
}, },
() => "查看代码", ]
),
}, onMounted(loadData)
] </script>
onMounted(loadData) <template>
</script> <n-flex vertical>
<n-flex justify="space-between" align="center">
<template> <n-flex align="center">
<n-flex vertical> <h2 style="margin: 0">比赛辅助检查</h2>
<n-flex justify="space-between" align="center"> <n-tag type="info" size="large"> 总计: {{ stats.total }} </n-tag>
<n-flex align="center"> <n-tag type="success" size="large"> 已检查: {{ stats.checked }} </n-tag>
<h2 style="margin: 0">比赛辅助检查</h2> <n-tag type="warning" size="large">
<n-tag type="info" size="large"> 未检查: {{ stats.unchecked }}
总计: {{ stats.total }} </n-tag>
</n-tag> </n-flex>
<n-tag type="success" size="large"> <n-button
已检查: {{ stats.checked }} type="primary"
</n-tag> :disabled="stats.unchecked === 0"
<n-tag type="warning" size="large"> @click="markAllAsChecked"
未检查: {{ stats.unchecked }} >
</n-tag> 标记全部为已检查
</n-flex> </n-button>
<n-button </n-flex>
type="primary"
:disabled="stats.unchecked === 0" <n-alert type="info" style="margin-bottom: 16px">
@click="markAllAsChecked" <template #header>使用说明</template>
> 此工具用于赛后人工审核代码检查是否存在抄袭作弊等行为请逐个查看通过AC的提交代码检查完成后勾选"已检查"
标记全部为已检查 </n-alert>
</n-button>
</n-flex> <n-flex align="center" style="margin-bottom: 16px">
<n-input
<n-alert type="info" style="margin-bottom: 16px"> v-model:value="query.username"
<template #header>使用说明</template> placeholder="筛选用户名"
此工具用于赛后人工审核代码检查是否存在抄袭作弊等行为请逐个查看通过AC的提交代码检查完成后勾选"已检查" style="width: 150px"
</n-alert> clearable
/>
<n-flex align="center" style="margin-bottom: 16px"> <n-input
<n-input v-model:value="query.problemId"
v-model:value="query.username" placeholder="筛选题目"
placeholder="筛选用户名" style="width: 150px"
style="width: 150px" clearable
clearable />
/> <n-select
<n-input v-model:value="query.checked"
v-model:value="query.problemId" :options="checkedOptions"
placeholder="筛选题目" style="width: 120px"
style="width: 150px" />
clearable </n-flex>
/>
<n-select <n-data-table
v-model:value="query.checked" :columns="columns"
:options="checkedOptions" :data="filteredSubmissions"
style="width: 120px" :pagination="{ pageSize: 20 }"
/> :bordered="false"
</n-flex> />
<n-data-table <n-modal
:columns="columns" v-model:show="codePanel"
:data="filteredSubmissions" preset="card"
:pagination="{ pageSize: 20 }" :style="{ maxWidth: isDesktop && '70vw', maxHeight: '80vh' }"
:bordered="false" :content-style="{ overflow: 'auto' }"
/> title="代码详情"
>
<n-modal <SubmissionDetail
v-model:show="codePanel" v-if="currentSubmission"
preset="card" :submission="currentSubmission"
:style="{ maxWidth: isDesktop && '70vw', maxHeight: '80vh' }" :problemID="currentSubmission.problem_display_id"
:content-style="{ overflow: 'auto' }" :submissionID="currentSubmission.id"
title="代码详情" hideList
> />
<SubmissionDetail </n-modal>
v-if="currentSubmission" </n-flex>
:submission="currentSubmission" </template>
:problemID="currentSubmission.problem_display_id"
:submissionID="currentSubmission.id" <style scoped>
hideList :deep(.n-data-table) {
/> margin-top: 16px;
</n-modal> }
</n-flex> </style>
</template>
<style scoped>
:deep(.n-data-table) {
margin-top: 16px;
}
</style>

View File

@@ -1,71 +1,49 @@
<template> <template>
<n-grid :cols="2" :x-gap="24" v-if="!!tutorial.id"> <n-grid :cols="5" :x-gap="16" v-if="tutorial.id">
<n-gi :span="1"> <n-gi :span="1">
<n-flex vertical> <n-card title="目录" :bordered="false" size="small">
<n-flex align="center"> <n-scrollbar :style="{ maxHeight: 'calc(100vh - 180px)' }">
<n-button text :disabled="step == 1" @click="prev"> <n-list hoverable clickable>
<Icon :width="30" icon="pepicons-pencil:arrow-left"></Icon> <n-list-item
</n-button> v-for="(item, index) in titles"
<n-dropdown size="large" :options="menu" trigger="click"> :key="item.id"
<n-button tertiary style="flex: 1" size="large"> @click="goToLesson(index + 1)"
<n-flex align="center"> >
<span style="font-weight: bold"> <n-text
{{ title }} :type="step === index + 1 ? 'primary' : undefined"
</span> :strong="step === index + 1"
<Icon :width="24" icon="proicons:chevron-down"></Icon>
</n-flex>
</n-button>
</n-dropdown>
<n-button text :disabled="step == titles.length" @click="next">
<Icon :width="30" icon="pepicons-pencil:arrow-right"></Icon>
</n-button>
</n-flex>
<n-flex vertical size="large">
<MdPreview
preview-theme="vuepress"
:theme="isDark ? 'dark' : 'light'"
:model-value="tutorial.content"
/>
<n-flex justify="space-between">
<div style="flex: 1">
<n-button
block
style="height: 40px"
v-if="step !== 1"
@click="prev"
> >
上一课时 {{ index + 1 }}. {{ item.title }}
</n-button> </n-text>
</div> </n-list-item>
<div style="flex: 1"> </n-list>
<n-button </n-scrollbar>
block </n-card>
style="height: 40px"
v-if="step !== titles.length"
@click="next"
>
下一课时
</n-button>
</div>
</n-flex>
</n-flex>
</n-flex>
</n-gi> </n-gi>
<n-gi :span="1">
<n-flex vertical> <n-gi :span="tutorial.code ? 2 : 4">
<CodeEditor <n-card
v-show="!!tutorial.code" :title="`第 ${step} 课:${titles[step - 1]?.title}`"
language="Python3" :bordered="false"
v-model="tutorial.code" size="small"
>
<MdPreview
preview-theme="vuepress"
:theme="isDark ? 'dark' : 'light'"
:model-value="tutorial.content"
/> />
</n-flex> </n-card>
</n-gi>
<n-gi :span="2" v-if="tutorial.code">
<n-card title="示例代码" :bordered="false" size="small">
<CodeEditor language="Python3" v-model="tutorial.code" />
</n-card>
</n-gi> </n-gi>
</n-grid> </n-grid>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from "@iconify/vue"
import { MdPreview } from "md-editor-v3" import { MdPreview } from "md-editor-v3"
import "md-editor-v3/lib/preview.css" import "md-editor-v3/lib/preview.css"
import { Tutorial } from "utils/types" import { Tutorial } from "utils/types"
@@ -94,23 +72,11 @@ const tutorial = ref<Partial<Tutorial>>({
}) })
const titles = ref<{ id: number; title: string }[]>([]) const titles = ref<{ id: number; title: string }[]>([])
const title = computed(
() => `${step.value} 课:${titles.value[step.value - 1].title}`,
)
const menu = computed<DropdownOption[]>(() => { function goToLesson(lessonNumber: number) {
return titles.value.map((item, index) => { const dest = lessonNumber.toString().padStart(2, "0")
const id = (index + 1).toString().padStart(2, "0") router.push("/learn/" + dest)
const prefix = `${index + 1} 课:` }
return {
key: id,
label: prefix + item.title,
props: {
onClick: () => router.push(`/learn/${id}`),
},
}
})
})
async function init() { async function init() {
const res1 = await getTutorials() const res1 = await getTutorials()
@@ -121,18 +87,6 @@ async function init() {
tutorial.value = res2.data tutorial.value = res2.data
} }
function next() {
if (step.value === titles.value.length) return
const dest = (step.value + 1).toString().padStart(2, "0")
router.push("/learn/" + dest)
}
function prev() {
if (step.value === 1) return
const dest = (step.value - 1).toString().padStart(2, "0")
router.push("/learn/" + dest)
}
watch( watch(
() => route.params.step, () => route.params.step,
async () => { async () => {
@@ -143,8 +97,8 @@ watch(
) )
</script> </script>
<style> <style scoped>
.md-editor-preview .md-editor-code .md-editor-code-head { :deep(.md-editor-preview .md-editor-code .md-editor-code-head) {
z-index: 100; z-index: 100;
} }
</style> </style>

View File

@@ -86,9 +86,7 @@ const languageOptions: DropdownOption[] = problem.value!.languages.map(
:options="languageOptions" :options="languageOptions"
@update:value="changeLanguage" @update:value="changeLanguage"
/> />
<n-button @click="copy"> <n-button @click="copy">复制代码</n-button>
复制代码
</n-button>
<n-button @click="reset">重置代码</n-button> <n-button @click="reset">重置代码</n-button>
<n-button type="primary" secondary @click="runCode"> <n-button type="primary" secondary @click="runCode">
运行代码 运行代码

View File

@@ -204,116 +204,138 @@ function type(status: ProblemStatus) {
</div> </div>
</template> </template>
<style> <style scoped>
.problemContent .problemTitle { .problemContent {
--border-color-light: rgb(239, 239, 245);
--bg-code-light: rgb(250, 250, 252);
--bg-code-dark: rgb(24, 24, 28);
--bg-table-light: rgba(250, 250, 252, 1);
--bg-table-dark: rgba(38, 38, 42, 1);
--border-color-dark: rgba(255, 255, 255, 0.09);
--blockquote-color: #7b7b7b;
--blockquote-border: #bbbec4;
--link-color: #18a058;
--transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.problemTitle {
margin: 0; margin: 0;
} }
.problemContent .title { .title {
font-size: 20px; font-size: 20px;
margin: 12px 0; margin: 12px 0;
} }
.problemContent .content { .testcase {
font-size: 14px;
white-space: pre;
font-family: "Monaco", monospace;
}
.status-alert {
margin-bottom: 16px;
}
.content {
font-size: 16px; font-size: 16px;
line-height: 2; line-height: 2;
} }
.problemContent .testcase { /* 针对 v-html 渲染内容的深度样式 */
font-size: 14px; .content :deep(p) {
white-space: pre;
font-family: "Monaco";
}
.problemContent .status-alert {
margin-bottom: 16px;
}
.problemContent .content > p {
margin: 0; margin: 0;
} }
.problemContent .content > blockquote { .content :deep(blockquote) {
border-left: 3px solid #bbbec4; border-left: 3px solid var(--blockquote-border);
padding-left: 10px; padding-left: 10px;
margin: 10px 0; margin: 10px 0;
color: #7b7b7b; color: var(--blockquote-color);
} }
.problemContent .content > pre { .content :deep(pre) {
width: 100%; width: 100%;
background-color: rgb(250, 250, 252); background-color: var(--bg-code-light);
border: 1px solid rgb(239, 239, 245); border: 1px solid var(--border-color-light);
word-break: break-word; word-break: break-word;
box-sizing: border-box; box-sizing: border-box;
transition: transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color var(--transition),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color var(--transition);
} }
.problemContent .content > pre > code { .content :deep(pre code) {
font-family: "Monaco"; font-family: "Monaco", monospace;
} }
.dark .problemContent .content > pre { .dark .content :deep(pre) {
background-color: rgb(24, 24, 28); background-color: var(--bg-code-dark);
border-color: rgba(255, 255, 255, 0.09); border-color: var(--border-color-dark);
} }
.problemContent .content > table { .content :deep(table) {
width: 100%; width: 100%;
border-spacing: 0; border-spacing: 0;
border: 1px solid rgba(239, 239, 245, 1); border: 1px solid var(--border-color-light);
transition: transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color var(--transition),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color var(--transition);
} }
.problemContent .content > table th { .content :deep(table th) {
background-color: rgba(250, 250, 252, 1); background-color: var(--bg-table-light);
padding: 8px;
transition: transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color var(--transition),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color var(--transition);
} }
.dark .problemContent .content > table th { .dark .content :deep(table th) {
background-color: rgba(38, 38, 42, 1); background-color: var(--bg-table-dark);
} }
.problemContent .content > table td { .content :deep(table td) {
padding: 8px; padding: 8px;
} }
.problemContent .content > table td, .content :deep(table td),
.problemContent .content > table th { .content :deep(table th) {
border-right: 1px solid rgba(239, 239, 245, 1); border-right: 1px solid var(--border-color-light);
border-bottom: 1px solid rgba(239, 239, 245, 1); border-bottom: 1px solid var(--border-color-light);
} }
.problemContent .content > table th:last-child, .content :deep(table th:last-child),
.problemContent .content > table td:last-child { .content :deep(table td:last-child) {
border-right: 0px solid rgba(239, 239, 245, 1); border-right: none;
} }
.problemContent .content > table tr:last-child td { .content :deep(table tr:last-child td) {
border-bottom: 0px solid rgba(239, 239, 245, 1); border-bottom: none;
} }
.problemContent .content > p > code { .content :deep(p code) {
font-size: 90%; font-size: 90%;
padding: 2px 5px; padding: 2px 5px;
margin: 0px 4px; margin: 0 4px;
background-color: rgba(27, 31, 35, 0.05); background-color: rgba(27, 31, 35, 0.05);
border-radius: 3px; border-radius: 3px;
line-height: 1.5; line-height: 1.5;
font-family: "Monaco", monospace;
} }
.problemContent .content img { .content :deep(img) {
max-width: 100% !important; max-width: 100%;
height: 100% !important; height: auto;
} }
.problemContent .content a { .content :deep(a) {
color: #18a058; color: var(--link-color);
text-decoration: none;
transition: opacity var(--transition);
}
.content :deep(a:hover) {
opacity: 0.8;
} }
</style> </style>

View File

@@ -8,7 +8,7 @@ export const useConfigStore = defineStore("config", () => {
website_name_shortcut: "", website_name_shortcut: "",
website_footer: "", website_footer: "",
submission_list_show_all: true, submission_list_show_all: true,
allow_register: true, allow_register: false,
class_list: [], class_list: [],
}) })
async function getConfig() { async function getConfig() {