fix.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import { useContestStore } from "oj/store/contest"
|
import { useContestStore } from "oj/store/contest"
|
||||||
import ContestTypeVue from "~/shared/ContestType.vue"
|
import ContestType from "~/shared/ContestType.vue"
|
||||||
|
|
||||||
const contestStore = useContestStore()
|
const contestStore = useContestStore()
|
||||||
</script>
|
</script>
|
||||||
@@ -27,7 +27,7 @@ const contestStore = useContestStore()
|
|||||||
{{ parseTime(contestStore.contest.end_time, "YYYY年M月D日 hh:mm:ss") }}
|
{{ parseTime(contestStore.contest.end_time, "YYYY年M月D日 hh:mm:ss") }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="比赛类型">
|
<n-descriptions-item label="比赛类型">
|
||||||
<ContestTypeVue :contest="contestStore.contest" />
|
<ContestType :contest="contestStore.contest" />
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="发起人">
|
<n-descriptions-item label="发起人">
|
||||||
{{ contestStore.contest.created_by.username }}
|
{{ contestStore.contest.created_by.username }}
|
||||||
|
|||||||
@@ -29,14 +29,17 @@ const passwordFormVisible = computed(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="contestStore.contest">
|
<n-space vertical v-if="contestStore.contest">
|
||||||
<n-space class="title" align="center" justify="space-between">
|
<n-space align="center" justify="space-between">
|
||||||
<n-space align="center">
|
<n-space align="center">
|
||||||
<h2 class="contestTitle">{{ contestStore.contest.title }}</h2>
|
<h2 class="contestTitle">{{ contestStore.contest.title }}</h2>
|
||||||
<n-icon size="large" v-if="contestStore.isPrivate" class="lockIcon">
|
<n-icon size="large" v-if="contestStore.isPrivate" class="lockIcon">
|
||||||
<i-ep-lock />
|
<i-ep-lock />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<n-tag :type="CONTEST_STATUS[contestStore.contestStatus]['type']">
|
<n-tag
|
||||||
|
size="small"
|
||||||
|
:type="CONTEST_STATUS[contestStore.contestStatus]['type']"
|
||||||
|
>
|
||||||
{{ contestStore.countdown }}
|
{{ contestStore.countdown }}
|
||||||
</n-tag>
|
</n-tag>
|
||||||
</n-space>
|
</n-space>
|
||||||
@@ -67,14 +70,10 @@ const passwordFormVisible = computed(
|
|||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.title {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contestTitle {
|
.contestTitle {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -119,17 +119,20 @@ function rowProps(row: Contest) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<n-form label-placement="left" :inline="isDesktop">
|
<n-space>
|
||||||
<n-form-item label="状态">
|
<n-form :show-feedback="false" label-placement="left" inline>
|
||||||
|
<n-form-item label="比赛状态">
|
||||||
<n-select
|
<n-select
|
||||||
class="select"
|
class="select"
|
||||||
:options="options"
|
:options="options"
|
||||||
v-model:value="query.status"
|
v-model:value="query.status"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="搜索比赛标题">
|
<n-form-item label="搜索">
|
||||||
<n-input placeholder="输入后回车或点击搜索" clearable @change="search" />
|
<n-input clearable @change="search" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<n-form label-placement="left" inline>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button @click="search(query.keyword)">搜索</n-button>
|
<n-button @click="search(query.keyword)">搜索</n-button>
|
||||||
@@ -137,13 +140,8 @@ function rowProps(row: Contest) {
|
|||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-data-table
|
</n-space>
|
||||||
size="small"
|
<n-data-table striped :columns="columns" :data="data" :row-props="rowProps" />
|
||||||
striped
|
|
||||||
:columns="columns"
|
|
||||||
:data="data"
|
|
||||||
:row-props="rowProps"
|
|
||||||
/>
|
|
||||||
<Pagination
|
<Pagination
|
||||||
v-model:limit="query.limit"
|
v-model:limit="query.limit"
|
||||||
v-model:page="query.page"
|
v-model:page="query.page"
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ function rowProps(row: ProblemFiltered) {
|
|||||||
<template>
|
<template>
|
||||||
<n-data-table
|
<n-data-table
|
||||||
striped
|
striped
|
||||||
size="small"
|
|
||||||
:data="contestStore.problems"
|
:data="contestStore.problems"
|
||||||
:columns="problemsColumns"
|
:columns="problemsColumns"
|
||||||
:row-props="rowProps"
|
:row-props="rowProps"
|
||||||
|
|||||||
@@ -165,7 +165,6 @@ onMounted(() => {
|
|||||||
striped
|
striped
|
||||||
:single-line="false"
|
:single-line="false"
|
||||||
:scroll-x="1200"
|
:scroll-x="1200"
|
||||||
size="small"
|
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="data"
|
:data="data"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Promotion, CloseBold, Select } from "@element-plus/icons-vue"
|
|
||||||
import Copy from "~/shared/Copy.vue"
|
import Copy from "~/shared/Copy.vue"
|
||||||
import { code } from "oj/composables/code"
|
import { code } from "oj/composables/code"
|
||||||
import { SOURCES } from "utils/constants"
|
import { SOURCES } from "utils/constants"
|
||||||
@@ -78,18 +77,22 @@ async function test(sample: Sample, index: number) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const icon = (status: ProblemStatus) =>
|
function label(status: ProblemStatus, loading: boolean) {
|
||||||
({
|
if (loading) return "测试中"
|
||||||
not_test: Promotion,
|
return {
|
||||||
failed: CloseBold,
|
not_test: "测试",
|
||||||
passed: Select,
|
failed: "不通过",
|
||||||
}[status])
|
passed: "通过",
|
||||||
const type = (status: ProblemStatus) =>
|
}[status]
|
||||||
({
|
}
|
||||||
|
|
||||||
|
function type(status: ProblemStatus) {
|
||||||
|
return {
|
||||||
not_test: "",
|
not_test: "",
|
||||||
failed: "error",
|
failed: "error",
|
||||||
passed: "success",
|
passed: "success",
|
||||||
}[status] as "warning" | "error" | "success")
|
}[status] as "warning" | "error" | "success"
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -120,15 +123,12 @@ const type = (status: ProblemStatus) =>
|
|||||||
<n-tooltip trigger="hover">
|
<n-tooltip trigger="hover">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-button
|
<n-button
|
||||||
|
size="small"
|
||||||
:type="type(sample.status)"
|
:type="type(sample.status)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:loading="sample.loading"
|
|
||||||
circle
|
|
||||||
@click="test(sample, index)"
|
@click="test(sample, index)"
|
||||||
>
|
>
|
||||||
<template #icon>
|
{{ label(sample.status, sample.loading) }}
|
||||||
<component :is="icon(sample.status)"></component>
|
|
||||||
</template>
|
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
点击测试
|
点击测试
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const options = {
|
|||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item :span="3" label="标签">
|
<n-descriptions-item :span="3" label="标签">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-tag type="info" v-for="tag in problem.tags" :key="tag">
|
<n-tag size="small" type="info" v-for="tag in problem.tags" :key="tag">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</n-tag>
|
</n-tag>
|
||||||
</n-space>
|
</n-space>
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ watch(
|
|||||||
<n-card v-if="msg" embedded class="msg">{{ msg }}</n-card>
|
<n-card v-if="msg" embedded class="msg">{{ msg }}</n-card>
|
||||||
<n-data-table
|
<n-data-table
|
||||||
v-if="infoTable.length"
|
v-if="infoTable.length"
|
||||||
size="small"
|
|
||||||
striped
|
striped
|
||||||
:data="infoTable"
|
:data="infoTable"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { useUserStore } from "~/shared/store/user"
|
import { useUserStore } from "~/shared/store/user"
|
||||||
import { filterEmptyValue, getTagColor } from "utils/functions"
|
import { filterEmptyValue, getTagColor } from "utils/functions"
|
||||||
import { ProblemFiltered } from "utils/types"
|
import { ProblemFiltered } from "utils/types"
|
||||||
import { isMobile } from "~/shared/composables/breakpoints"
|
|
||||||
import { getProblemList, getRandomProblemID } from "oj/api"
|
import { getProblemList, getRandomProblemID } from "oj/api"
|
||||||
import Pagination from "~/shared/Pagination.vue"
|
import Pagination from "~/shared/Pagination.vue"
|
||||||
import { DataTableColumn, NSpace, NTag } from "naive-ui"
|
import { DataTableColumn, NSpace, NTag } from "naive-ui"
|
||||||
@@ -169,8 +168,8 @@ function rowProps(row: ProblemFiltered) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-space :vertical="isMobile">
|
<n-space>
|
||||||
<n-form inline label-placement="left">
|
<n-form :show-feedback="false" inline label-placement="left">
|
||||||
<n-form-item label="难度">
|
<n-form-item label="难度">
|
||||||
<n-select
|
<n-select
|
||||||
class="select"
|
class="select"
|
||||||
@@ -178,12 +177,8 @@ function rowProps(row: ProblemFiltered) {
|
|||||||
:options="difficultyOptions"
|
:options="difficultyOptions"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item label="搜索">
|
||||||
<n-input
|
<n-input clearable @change="search" />
|
||||||
placeholder="输入编号或标题后回车"
|
|
||||||
clearable
|
|
||||||
@change="search"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-form inline label-placement="left">
|
<n-form inline label-placement="left">
|
||||||
@@ -212,7 +207,6 @@ function rowProps(row: ProblemFiltered) {
|
|||||||
<n-data-table
|
<n-data-table
|
||||||
class="table"
|
class="table"
|
||||||
striped
|
striped
|
||||||
size="small"
|
|
||||||
:data="problems"
|
:data="problems"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:row-props="rowProps"
|
:row-props="rowProps"
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ const data = computed(() => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
text: "全校前十名的用户(不包括超管)",
|
text: "全校前十名的用户(不包括超管)",
|
||||||
@@ -79,10 +80,13 @@ const options = {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Bar class="chart" :data="data" :options="options" />
|
<div class="chart">
|
||||||
|
<Bar :data="data" :options="options" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chart {
|
.chart {
|
||||||
margin-bottom: 24px;
|
height: 400px;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ onMounted(listRanks)
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Chart v-if="!!chart.length" :rankData="chart" />
|
<Chart v-if="!!chart.length" :rankData="chart" />
|
||||||
<n-data-table striped size="small" :data="data" :columns="columns" />
|
<n-data-table striped :data="data" :columns="columns" />
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
:total="total"
|
||||||
v-model:page="query.page"
|
v-model:page="query.page"
|
||||||
|
|||||||
@@ -67,7 +67,12 @@ onMounted(init)
|
|||||||
</n-alert>
|
</n-alert>
|
||||||
<n-card embedded>
|
<n-card embedded>
|
||||||
<n-space justify="end">
|
<n-space justify="end">
|
||||||
<n-button type="primary" @click="handleCopy(submission!.code)">
|
<n-button
|
||||||
|
quaternary
|
||||||
|
class="copyBtn"
|
||||||
|
type="primary"
|
||||||
|
@click="handleCopy(submission!.code)"
|
||||||
|
>
|
||||||
{{ copied ? "已复制" : "复制代码" }}
|
{{ copied ? "已复制" : "复制代码" }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
@@ -90,4 +95,8 @@ onMounted(init)
|
|||||||
.code {
|
.code {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copyBtn {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ const columns = computed(() => {
|
|||||||
{
|
{
|
||||||
title: "编号",
|
title: "编号",
|
||||||
key: "id",
|
key: "id",
|
||||||
|
minWidth: 160,
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
if (row.show_link) {
|
if (row.show_link) {
|
||||||
return h(
|
return h(
|
||||||
@@ -215,6 +216,7 @@ const columns = computed(() => {
|
|||||||
h(
|
h(
|
||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
|
quaternary: true,
|
||||||
size: "small",
|
size: "small",
|
||||||
type: "primary",
|
type: "primary",
|
||||||
onClick: () => rejudge(row.id),
|
onClick: () => rejudge(row.id),
|
||||||
@@ -227,7 +229,8 @@ const columns = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<n-form :inline="isDesktop" label-placement="left">
|
<n-space>
|
||||||
|
<n-form :show-feedback="false" inline label-placement="left">
|
||||||
<n-form-item label="提交状态">
|
<n-form-item label="提交状态">
|
||||||
<n-select
|
<n-select
|
||||||
class="select"
|
class="select"
|
||||||
@@ -238,8 +241,10 @@ const columns = computed(() => {
|
|||||||
<n-form-item label="只看自己">
|
<n-form-item label="只看自己">
|
||||||
<n-switch v-model:value="query.myself" />
|
<n-switch v-model:value="query.myself" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<n-form inline label-placement="left">
|
||||||
<n-form-item label="搜索用户">
|
<n-form-item label="搜索用户">
|
||||||
<n-input @change="search" clearable placeholder="输入后回车或点击搜索" />
|
<n-input clearable @change="search" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
<n-space>
|
<n-space>
|
||||||
@@ -248,7 +253,8 @@ const columns = computed(() => {
|
|||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-data-table striped size="small" :columns="columns" :data="submissions" />
|
</n-space>
|
||||||
|
<n-data-table striped :columns="columns" :data="submissions" />
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
:total="total"
|
||||||
v-model:limit="query.limit"
|
v-model:limit="query.limit"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { FormRules } from "naive-ui"
|
|||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const loginRef = ref()
|
const loginRef = ref()
|
||||||
const [isLoading] = useToggle()
|
const [isLoading, toggleLoading] = useToggle()
|
||||||
const msg = ref("")
|
const msg = ref("")
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
username: "",
|
username: "",
|
||||||
@@ -21,11 +21,11 @@ const rules: FormRules = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
loginRef.value?.validate(async (errors: FormRules | undefined) => {
|
loginRef.value!.validate(async (errors: FormRules | undefined) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
try {
|
try {
|
||||||
msg.value = ""
|
msg.value = ""
|
||||||
isLoading.value = true
|
toggleLoading(true)
|
||||||
await login(form)
|
await login(form)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.data === "Your account has been disabled") {
|
if (err.data === "Your account has been disabled") {
|
||||||
@@ -36,7 +36,7 @@ async function submit() {
|
|||||||
msg.value = "无法登录"
|
msg.value = "无法登录"
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
toggleLoading(false)
|
||||||
}
|
}
|
||||||
if (!msg.value) {
|
if (!msg.value) {
|
||||||
toggleLogin(false)
|
toggleLogin(false)
|
||||||
@@ -85,7 +85,7 @@ function goSignup() {
|
|||||||
<n-button type="primary" :loading="isLoading" @click="submit">
|
<n-button type="primary" :loading="isLoading" @click="submit">
|
||||||
登录
|
登录
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button @click="goSignup">没有账号,立即注册</n-button>
|
<n-button @click="goSignup">没有账号?立即注册</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ watch(page, () => emit("update:page", page))
|
|||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.margin {
|
.margin {
|
||||||
margin-top: 24px;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
.right {
|
.right {
|
||||||
float: right;
|
float: right;
|
||||||
|
|||||||
@@ -1,16 +1,43 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormRules } from "naive-ui"
|
import { getCaptcha, signup, login } from "./api"
|
||||||
import { signupModal, toggleLogin, toggleSignup } from "./composables/modal"
|
import { signupModal, toggleLogin, toggleSignup } from "./composables/modal"
|
||||||
|
import { useUserStore } from "./store/user"
|
||||||
|
import type { FormItemRule, FormRules } from "naive-ui"
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const signupRef = ref()
|
||||||
|
const captchaSrc = ref("")
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
username: "",
|
username: "",
|
||||||
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
passwordAgain: "",
|
passwordAgain: "",
|
||||||
email: "",
|
captcha: "",
|
||||||
})
|
})
|
||||||
const rules: FormRules = {}
|
|
||||||
|
|
||||||
const [isLoading] = useToggle()
|
const rules: FormRules = {
|
||||||
|
username: [{ required: true, message: "用户名必填", trigger: "blur" }],
|
||||||
|
email: [{ required: true, message: "邮箱必填", trigger: "blur" }],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: "密码必填", trigger: "blur" },
|
||||||
|
{ min: 6, max: 20, message: "长度在 6 到 20 位之间", trigger: "input" },
|
||||||
|
],
|
||||||
|
passwordAgain: [
|
||||||
|
{ required: true, message: "密码必填", trigger: "blur" },
|
||||||
|
{ min: 6, max: 20, message: "长度在 6 到 20 位之间", trigger: "input" },
|
||||||
|
{
|
||||||
|
validator: (_: FormItemRule, value: string) => value === form.password,
|
||||||
|
message: "两次密码输入不一致",
|
||||||
|
trigger: "blur",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
captcha: [
|
||||||
|
{ required: true, message: "验证码必填", trigger: "blur", min: 1, max: 10 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const [isLoading, toggleLoading] = useToggle()
|
||||||
const msg = ref("")
|
const msg = ref("")
|
||||||
|
|
||||||
function goLogin() {
|
function goLogin() {
|
||||||
@@ -18,7 +45,50 @@ function goLogin() {
|
|||||||
toggleSignup(false)
|
toggleSignup(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit() {}
|
function submit() {
|
||||||
|
signupRef.value!.validate(async (errors: FormRules | undefined) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
msg.value = ""
|
||||||
|
toggleLoading(true)
|
||||||
|
await signup({
|
||||||
|
username: form.username,
|
||||||
|
email: form.email,
|
||||||
|
password: form.password,
|
||||||
|
captcha: form.captcha,
|
||||||
|
})
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.data === "Invalid captcha") {
|
||||||
|
msg.value = "验证码不正确"
|
||||||
|
} else if (err.data === "Username already exists") {
|
||||||
|
msg.value = "用户名已存在"
|
||||||
|
} else if (err.data === "Email already exists") {
|
||||||
|
msg.value = "邮箱已存在"
|
||||||
|
} else {
|
||||||
|
msg.value = "无法注册"
|
||||||
|
}
|
||||||
|
getCaptchaSrc()
|
||||||
|
form.captcha = ""
|
||||||
|
} finally {
|
||||||
|
toggleLoading(false)
|
||||||
|
}
|
||||||
|
if (!msg.value) {
|
||||||
|
toggleSignup(false)
|
||||||
|
await login({ username: form.username, password: form.password })
|
||||||
|
userStore.getMyProfile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCaptchaSrc() {
|
||||||
|
const res = await getCaptcha()
|
||||||
|
captchaSrc.value = res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(signupModal, (v) => {
|
||||||
|
if (v) getCaptchaSrc()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -55,7 +125,7 @@ function submit() {}
|
|||||||
name="signup password"
|
name="signup password"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="确认密码" path="password">
|
<n-form-item label="确认密码" path="passwordAgain">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="form.passwordAgain"
|
v-model:value="form.passwordAgain"
|
||||||
clearable
|
clearable
|
||||||
@@ -63,11 +133,21 @@ function submit() {}
|
|||||||
name="signup password again"
|
name="signup password again"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
<n-form-item label="验证码" path="captcha">
|
||||||
|
<n-space>
|
||||||
|
<n-input
|
||||||
|
v-model:value="form.captcha"
|
||||||
|
clearable
|
||||||
|
name="signup captcha"
|
||||||
|
/>
|
||||||
|
<img class="captcha" :src="captchaSrc" @click="getCaptchaSrc" />
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
<n-alert v-if="msg" type="error" :show-icon="false"> {{ msg }}</n-alert>
|
<n-alert v-if="msg" type="error" :show-icon="false"> {{ msg }}</n-alert>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button type="primary" :loading="isLoading" @click="submit">
|
<n-button type="primary" :loading="isLoading" @click="submit">
|
||||||
登录
|
注册
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button @click="goLogin">已经注册?现在登录</n-button>
|
<n-button @click="goLogin">已经注册?现在登录</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
@@ -76,4 +156,9 @@ function submit() {}
|
|||||||
</n-modal>
|
</n-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.captcha {
|
||||||
|
height: 34px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ export function login(data: { username: string; password: string }) {
|
|||||||
return http.post("login", data)
|
return http.post("login", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function signup(data: {
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
captcha: string
|
||||||
|
}) {
|
||||||
|
return http.post("register", data)
|
||||||
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return http.get("logout")
|
return http.get("logout")
|
||||||
}
|
}
|
||||||
@@ -16,3 +25,7 @@ export function getProfile(username: string = "") {
|
|||||||
export function getProblemTagList() {
|
export function getProblemTagList() {
|
||||||
return http.get<Tag[]>("problem/tags")
|
return http.get<Tag[]>("problem/tags")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCaptcha() {
|
||||||
|
return http.get("captcha")
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Header from "../Header.vue"
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-layout>
|
<n-layout position="absolute">
|
||||||
<n-layout-header bordered class="header">
|
<n-layout-header bordered class="header">
|
||||||
<Header />
|
<Header />
|
||||||
</n-layout-header>
|
</n-layout-header>
|
||||||
|
|||||||
Reference in New Issue
Block a user