refactor axios.
This commit is contained in:
99
package-lock.json
generated
99
package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"@monaco-editor/loader": "^1.3.2",
|
"@monaco-editor/loader": "^1.3.2",
|
||||||
"@vueuse/core": "^9.10.0",
|
"@vueuse/core": "^9.10.0",
|
||||||
|
"@vueuse/integrations": "^9.10.0",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
"element-plus": "^2.2.28",
|
"element-plus": "^2.2.28",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
@@ -778,6 +779,86 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vueuse/integrations": {
|
||||||
|
"version": "9.10.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-9.10.0.tgz",
|
||||||
|
"integrity": "sha512-MLGVbN3i9gRq3pb8VRZXgPvbNJcUUvgR5pmbc1QZj4Z1vvsvxam159AwWEJdyX2I39a1E7EkmBujtiXtVckO5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vueuse/core": "9.10.0",
|
||||||
|
"@vueuse/shared": "9.10.0",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"async-validator": "*",
|
||||||
|
"axios": "*",
|
||||||
|
"change-case": "*",
|
||||||
|
"drauu": "*",
|
||||||
|
"focus-trap": "*",
|
||||||
|
"fuse.js": "*",
|
||||||
|
"idb-keyval": "*",
|
||||||
|
"jwt-decode": "*",
|
||||||
|
"nprogress": "*",
|
||||||
|
"qrcode": "*",
|
||||||
|
"universal-cookie": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"async-validator": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"axios": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"change-case": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"drauu": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"focus-trap": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"fuse.js": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"idb-keyval": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"jwt-decode": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"nprogress": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"qrcode": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"universal-cookie": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/integrations/node_modules/vue-demi": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vueuse/metadata": {
|
"node_modules/@vueuse/metadata": {
|
||||||
"version": "9.10.0",
|
"version": "9.10.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
||||||
@@ -2639,6 +2720,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@vueuse/integrations": {
|
||||||
|
"version": "9.10.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vueuse/integrations/-/integrations-9.10.0.tgz",
|
||||||
|
"integrity": "sha512-MLGVbN3i9gRq3pb8VRZXgPvbNJcUUvgR5pmbc1QZj4Z1vvsvxam159AwWEJdyX2I39a1E7EkmBujtiXtVckO5g==",
|
||||||
|
"requires": {
|
||||||
|
"@vueuse/core": "9.10.0",
|
||||||
|
"@vueuse/shared": "9.10.0",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@vueuse/metadata": {
|
"@vueuse/metadata": {
|
||||||
"version": "9.10.0",
|
"version": "9.10.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.10.0.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"@monaco-editor/loader": "^1.3.2",
|
"@monaco-editor/loader": "^1.3.2",
|
||||||
"@vueuse/core": "^9.10.0",
|
"@vueuse/core": "^9.10.0",
|
||||||
|
"@vueuse/integrations": "^9.10.0",
|
||||||
"axios": "^1.2.2",
|
"axios": "^1.2.2",
|
||||||
"element-plus": "^2.2.28",
|
"element-plus": "^2.2.28",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "problem/:problemID",
|
path: "problem/:problemID",
|
||||||
component: () => import("./oj/problem/detail.vue"),
|
component: () => import("./oj/problem/detail.vue"),
|
||||||
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "status",
|
path: "status",
|
||||||
@@ -30,6 +31,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "status/:statusID",
|
path: "status/:statusID",
|
||||||
component: () => import("./oj/status/detail.vue"),
|
component: () => import("./oj/status/detail.vue"),
|
||||||
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "contest",
|
path: "contest",
|
||||||
@@ -39,6 +41,12 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "contest/:contestID",
|
path: "contest/:contestID",
|
||||||
component: () => import("./oj/contest/detail.vue"),
|
component: () => import("./oj/contest/detail.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "contest/:contestID/problem/:problemID",
|
||||||
|
component: () => import("./oj/problem/detail.vue"),
|
||||||
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "rank",
|
path: "rank",
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { getACRate } from "./../utils/functions"
|
import { getACRate } from "./../utils/functions"
|
||||||
import { DIFFICULTY } from "./../utils/constants"
|
import { DIFFICULTY } from "./../utils/constants"
|
||||||
|
import { Problem, LANGUAGE } from "./../utils/types"
|
||||||
import http from "./../utils/http"
|
import http from "./../utils/http"
|
||||||
|
import { useAxios } from "@vueuse/integrations/useAxios"
|
||||||
|
|
||||||
function filterResult(result: any) {
|
function filterResult(result: Problem) {
|
||||||
const newResult: any = {
|
const newResult: any = {
|
||||||
displayID: result._id,
|
_id: result._id,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
difficulty: DIFFICULTY[<"Low" | "Mid" | "High">result.difficulty],
|
difficulty: DIFFICULTY[result.difficulty],
|
||||||
tags: result.tags,
|
tags: result.tags,
|
||||||
submission: result.submission_number,
|
submission: result.submission_number,
|
||||||
rate: getACRate(result.accepted_number, result.submission_number),
|
rate: getACRate(result.accepted_number, result.submission_number),
|
||||||
@@ -46,7 +48,7 @@ export async function getProblemList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getProblemTagList() {
|
export function getProblemTagList() {
|
||||||
return http.get("problem/tags")
|
return useAxios<{ id: number; name: string }[]>("problem/tags", http)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomProblemID() {
|
export function getRandomProblemID() {
|
||||||
@@ -54,11 +56,20 @@ export function getRandomProblemID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getProblem(id: string) {
|
export function getProblem(id: string) {
|
||||||
return http.get("problem", {
|
return useAxios<Problem>("problem", { params: { problem_id: id } }, http)
|
||||||
params: { problem_id: id },
|
}
|
||||||
|
|
||||||
|
export function getSubmission(id: string) {
|
||||||
|
return http.get("submission", {
|
||||||
|
params: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWebsite() {
|
export function submitCode(data: {
|
||||||
return http.get("website")
|
problem_id: number
|
||||||
|
contest_id?: number
|
||||||
|
language: LANGUAGE
|
||||||
|
code: string
|
||||||
|
}) {
|
||||||
|
return http.post("submission", data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ onMounted(userStore.getMyProfile)
|
|||||||
<el-menu-item index="/status">提交</el-menu-item>
|
<el-menu-item index="/status">提交</el-menu-item>
|
||||||
<el-menu-item index="/rank">排名</el-menu-item>
|
<el-menu-item index="/rank">排名</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div v-if="userStore.isLoaded && !userStore.isAuthed" class="actions">
|
<div v-if="userStore.isFinished && !userStore.isAuthed" class="actions">
|
||||||
<el-button @click="loginStore.show">登录</el-button>
|
<el-button @click="loginStore.show">登录</el-button>
|
||||||
<el-button @click="signupStore.show">注册</el-button>
|
<el-button @click="signupStore.show">注册</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="userStore.isLoaded && userStore.isAuthed" class="actions">
|
<div v-if="userStore.isFinished && userStore.isAuthed" class="actions">
|
||||||
<el-dropdown @command="handleDropdown">
|
<el-dropdown @command="handleDropdown">
|
||||||
<el-button>{{ userStore.user.username }}</el-button>
|
<el-button>{{ userStore.user.username }}</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import loader, { Monaco } from "@monaco-editor/loader"
|
import loader, { Monaco } from "@monaco-editor/loader"
|
||||||
import { ref, onBeforeUnmount, onMounted, watch, reactive } from "vue"
|
import { ref, onBeforeUnmount, onMounted, watch, reactive, computed } from "vue"
|
||||||
import {
|
import {
|
||||||
LANGUAGE,
|
|
||||||
LANGUAGE_LABEL,
|
LANGUAGE_LABEL,
|
||||||
LANGUAGE_VALUE,
|
LANGUAGE_VALUE,
|
||||||
SOURCES,
|
SOURCES,
|
||||||
} from "../../../utils/constants"
|
} from "../../../utils/constants"
|
||||||
import { isMobile } from "../../../utils/breakpoints"
|
import { isMobile } from "../../../utils/breakpoints"
|
||||||
|
import { submitCode } from "../../api"
|
||||||
|
import { Problem } from "../../../utils/types"
|
||||||
|
|
||||||
const { problem } = defineProps<{
|
const { problem, contestID = "" } = defineProps<{
|
||||||
problem: {
|
contestID?: string
|
||||||
languages: Array<LANGUAGE>
|
problemID?: string
|
||||||
template: { [key in LANGUAGE]?: string }
|
problem: Problem
|
||||||
}
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
values: ref({ ...SOURCES }),
|
values: ref({ ...SOURCES }),
|
||||||
language: problem.languages[0] || "C",
|
language: problem.languages[0] || "C",
|
||||||
isMobile,
|
isMobile,
|
||||||
|
submissionId: "",
|
||||||
})
|
})
|
||||||
|
|
||||||
const monacoEditorRef = ref()
|
const monacoEditorRef = ref()
|
||||||
|
|
||||||
let monaco: Monaco
|
let monaco: Monaco
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
@@ -34,9 +33,7 @@ function reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(init)
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
monaco.editor.getModels().forEach((model) => model.dispose())
|
monaco.editor.getModels().forEach((model) => model.dispose())
|
||||||
@@ -77,6 +74,29 @@ async function init() {
|
|||||||
state.values[state.language] = monaco.editor.getModels()[0].getValue()
|
state.values[state.language] = monaco.editor.getModels()[0].getValue()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const submitDisabled = computed(() => {
|
||||||
|
const code = state.values[state.language]
|
||||||
|
return (
|
||||||
|
code.trim() === "" ||
|
||||||
|
code === problem.template[state.language] ||
|
||||||
|
code === SOURCES[state.language]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
const data = {
|
||||||
|
problem_id: problem.id,
|
||||||
|
language: state.language,
|
||||||
|
code: state.values[state.language],
|
||||||
|
}
|
||||||
|
if (contestID) {
|
||||||
|
//@ts-ignore
|
||||||
|
data.contest_id = parseInt(contestID)
|
||||||
|
}
|
||||||
|
const res = await submitCode(data)
|
||||||
|
state.submissionId = res.data.submission_id
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -107,7 +127,9 @@ async function init() {
|
|||||||
<el-form class="actions">
|
<el-form class="actions">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button>运行</el-button>
|
<el-button>运行</el-button>
|
||||||
<el-button type="primary">提交</el-button>
|
<el-button type="primary" :disabled="submitDisabled" @click="submit">
|
||||||
|
提交
|
||||||
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { problem } = defineProps(["problem"])
|
import { Problem } from "../../../utils/types"
|
||||||
|
|
||||||
|
const { problem } = defineProps<{ problem: Problem }>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DIFFICULTY } from "../../../utils/constants"
|
import { DIFFICULTY } from "../../../utils/constants"
|
||||||
import { getACRate, getTagColor } from "../../../utils/functions"
|
import { getACRate, getTagColor, parseTime } from "../../../utils/functions"
|
||||||
import { isDesktop } from "../../../utils/breakpoints"
|
import { isDesktop } from "../../../utils/breakpoints"
|
||||||
|
import { Problem } from "../../../utils/types"
|
||||||
|
|
||||||
const { problem } = defineProps(["problem"])
|
const { problem } = defineProps<{ problem: Problem }>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -14,19 +15,20 @@ const { problem } = defineProps(["problem"])
|
|||||||
<el-descriptions-item label="出题人">
|
<el-descriptions-item label="出题人">
|
||||||
{{ problem.created_by.username }}
|
{{ problem.created_by.username }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="难度">
|
<el-descriptions-item label="创建时间">
|
||||||
<el-tag disable-transitions :type="getTagColor(problem.difficulty)">
|
{{ parseTime(problem.create_time) }}
|
||||||
{{ DIFFICULTY[<"Low" | "Mid" | "High">problem.difficulty] }}
|
|
||||||
</el-tag>
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="时间限制">
|
<el-descriptions-item label="时间限制">
|
||||||
{{ problem.time_limit }}毫秒
|
{{ problem.time_limit }}毫秒
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="内存限制">
|
<el-descriptions-item label="内存限制">
|
||||||
{{ problem.memory_limit }}MB
|
{{ problem.memory_limit }}MB
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="类型">
|
<el-descriptions-item label="难度">
|
||||||
{{ problem.rule_type }}
|
<el-tag disable-transitions :type="getTagColor(problem.difficulty)">
|
||||||
|
{{ DIFFICULTY[problem.difficulty] }}
|
||||||
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="提交正确">
|
<el-descriptions-item label="提交正确">
|
||||||
|
|||||||
@@ -1,39 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue"
|
|
||||||
import { useRoute } from "vue-router"
|
|
||||||
import Editor from "./components/editor.vue"
|
import Editor from "./components/editor.vue"
|
||||||
import ProblemContent from "./components/problem-content.vue"
|
import ProblemContent from "./components/problem-content.vue"
|
||||||
import ProblemInfo from "./components/problem-info.vue"
|
import ProblemInfo from "./components/problem-info.vue"
|
||||||
import { getProblem } from "../api"
|
import { getProblem } from "../api"
|
||||||
import { isDesktop, isMobile } from "../../utils/breakpoints"
|
import { isDesktop, isMobile } from "../../utils/breakpoints"
|
||||||
|
|
||||||
const route = useRoute()
|
const { problemID = "", contestID = "" } = defineProps<{
|
||||||
const contestID = route.params.contestID as string
|
problemID?: string
|
||||||
const problemID = route.params.problemID as string
|
contestID?: string
|
||||||
|
}>()
|
||||||
const problem = ref({
|
const { data: problem, isFinished } = getProblem(problemID)
|
||||||
_id: "",
|
|
||||||
created_by: {},
|
|
||||||
io_mode: {},
|
|
||||||
languages: [],
|
|
||||||
samples: [],
|
|
||||||
statistic_info: {},
|
|
||||||
tags: [],
|
|
||||||
template: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const res = await getProblem(problemID)
|
|
||||||
problem.value = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-row v-if="problem._id">
|
<el-row v-if="isFinished && problem">
|
||||||
<el-col :span="isDesktop ? 12 : 24">
|
<el-col :span="isDesktop ? 12 : 24">
|
||||||
<el-tabs type="border-card">
|
<el-tabs type="border-card">
|
||||||
<el-tab-pane label="题目描述">
|
<el-tab-pane label="题目描述">
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ const router = useRouter()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const problems = ref([])
|
const problems = ref([])
|
||||||
const tags = ref(<{ id: number; name: string }[]>[])
|
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
|
|
||||||
|
const { data: tags } = getProblemTagList()
|
||||||
|
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
keyword: route.query.keyword || "",
|
keyword: route.query.keyword || "",
|
||||||
difficulty: route.query.difficulty || "",
|
difficulty: route.query.difficulty || "",
|
||||||
@@ -28,11 +29,6 @@ const query = reactive({
|
|||||||
limit: parseInt(<string>route.query.limit) || 10,
|
limit: parseInt(<string>route.query.limit) || 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
async function listTags() {
|
|
||||||
const res = await getProblemTagList()
|
|
||||||
tags.value = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listProblems() {
|
async function listProblems() {
|
||||||
query.keyword = route.query.keyword || ""
|
query.keyword = route.query.keyword || ""
|
||||||
query.difficulty = route.query.difficulty || ""
|
query.difficulty = route.query.difficulty || ""
|
||||||
@@ -77,7 +73,7 @@ async function getRandom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function goProblem(row: any) {
|
function goProblem(row: any) {
|
||||||
router.push("/problem/" + row.displayID)
|
router.push("/problem/" + row._id)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => query.page, routePush)
|
watch(() => query.page, routePush)
|
||||||
@@ -98,12 +94,9 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TODO: 这里会在登录时候执行两次,有BUG
|
// TODO: 这里会在登录时候执行两次,有BUG
|
||||||
watch(() => userStore.isLoaded && userStore.isAuthed, listProblems)
|
watch(() => userStore.isFinished && userStore.isAuthed, listProblems)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(listProblems)
|
||||||
listTags()
|
|
||||||
listProblems()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -158,11 +151,7 @@ onMounted(() => {
|
|||||||
/></el-icon>
|
/></el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column prop="_id" label="编号" :width="isDesktop ? 100 : 60" />
|
||||||
prop="displayID"
|
|
||||||
label="编号"
|
|
||||||
:width="isDesktop ? 100 : 60"
|
|
||||||
/>
|
|
||||||
<el-table-column prop="title" label="标题" />
|
<el-table-column prop="title" label="标题" />
|
||||||
<el-table-column label="难度" width="100">
|
<el-table-column label="难度" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import { useAxios } from "@vueuse/integrations/useAxios"
|
||||||
import http from "../utils/http"
|
import http from "../utils/http"
|
||||||
|
|
||||||
export function login(data: { username: string; password: string }) {
|
export function login(data: { username: string; password: string }) {
|
||||||
return http.post("login", data)
|
return useAxios("login", { method: "post", data }, http, {
|
||||||
|
immediate: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function logout() {
|
||||||
@@ -9,7 +12,7 @@ export function logout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getUserInfo(username: string) {
|
export function getUserInfo(username: string) {
|
||||||
return http.get("profile", {
|
return useAxios("profile", { method: "get", params: { username } }, http, {
|
||||||
params: { username },
|
immediate: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { defineStore } from "pinia"
|
import { defineStore } from "pinia"
|
||||||
import { computed, ref } from "vue"
|
import { computed } from "vue"
|
||||||
import {
|
import {
|
||||||
PROBLEM_PERMISSION,
|
PROBLEM_PERMISSION,
|
||||||
STORAGE_KEY,
|
STORAGE_KEY,
|
||||||
@@ -9,8 +9,7 @@ import storage from "../../utils/storage"
|
|||||||
import { getUserInfo } from "../api"
|
import { getUserInfo } from "../api"
|
||||||
|
|
||||||
export const useUserStore = defineStore("user", () => {
|
export const useUserStore = defineStore("user", () => {
|
||||||
const profile = ref<any>({})
|
const { data: profile, isFinished, execute } = getUserInfo("")
|
||||||
const isLoaded = ref(false)
|
|
||||||
const user = computed(() => profile.value.user || {})
|
const user = computed(() => profile.value.user || {})
|
||||||
const isAuthed = computed(() => !!user.value.email)
|
const isAuthed = computed(() => !!user.value.email)
|
||||||
const isAdminRole = computed(
|
const isAdminRole = computed(
|
||||||
@@ -26,10 +25,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
async function getMyProfile() {
|
async function getMyProfile() {
|
||||||
isLoaded.value = false
|
await execute()
|
||||||
const res = await getUserInfo("")
|
|
||||||
isLoaded.value = true
|
|
||||||
profile.value = res.data || {}
|
|
||||||
storage.set(STORAGE_KEY.AUTHED, !!user.value.email)
|
storage.set(STORAGE_KEY.AUTHED, !!user.value.email)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +35,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
profile,
|
profile,
|
||||||
isLoaded,
|
isFinished,
|
||||||
user,
|
user,
|
||||||
isAdminRole,
|
isAdminRole,
|
||||||
isSuperAdmin,
|
isSuperAdmin,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FormInstance } from "element-plus"
|
import { FormInstance } from "element-plus"
|
||||||
import { reactive, ref } from "vue"
|
import { computed, reactive, ref } from "vue"
|
||||||
import { useSignupStore } from "../../oj/stores/signup"
|
import { useSignupStore } from "../../oj/stores/signup"
|
||||||
import { login } from "../../shared/api"
|
import { login } from "../../shared/api"
|
||||||
import { useLoginStore } from "../stores/login"
|
import { useLoginStore } from "../stores/login"
|
||||||
@@ -9,8 +9,6 @@ import { useUserStore } from "../stores/user"
|
|||||||
const loginStore = useLoginStore()
|
const loginStore = useLoginStore()
|
||||||
const signupStore = useSignupStore()
|
const signupStore = useSignupStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const loading = ref(false)
|
|
||||||
const errorMessage = ref("")
|
|
||||||
const loginRef = ref<FormInstance>()
|
const loginRef = ref<FormInstance>()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
username: "",
|
username: "",
|
||||||
@@ -23,23 +21,19 @@ const rules = reactive({
|
|||||||
{ min: 6, max: 20, message: "长度在6到20位之间", trigger: "change" },
|
{ min: 6, max: 20, message: "长度在6到20位之间", trigger: "change" },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
const { isLoading, error, execute } = login(form)
|
||||||
|
const msg = computed(() => error.value && "用户名或密码不正确")
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
if (!loginRef.value) return
|
if (!loginRef.value) return
|
||||||
await loginRef.value.validate(async (valid) => {
|
const valid = await loginRef.value.validate()
|
||||||
if (valid) {
|
if (valid) {
|
||||||
loading.value = true
|
await execute()
|
||||||
errorMessage.value = ""
|
if (!error.value) {
|
||||||
try {
|
loginStore.hide()
|
||||||
await login(form)
|
userStore.getMyProfile()
|
||||||
loginStore.hide()
|
|
||||||
await userStore.getMyProfile()
|
|
||||||
} catch (err) {
|
|
||||||
errorMessage.value = "用户名或密码不正确"
|
|
||||||
}
|
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goSignup() {
|
function goSignup() {
|
||||||
@@ -75,17 +69,12 @@ function goSignup() {
|
|||||||
></el-input>
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" :loading="loading" @click="submit"
|
<el-button type="primary" :loading="isLoading" @click="submit">
|
||||||
>登录</el-button
|
登录
|
||||||
>
|
</el-button>
|
||||||
<el-button @click="goSignup">没有账号,立即注册</el-button>
|
<el-button @click="goSignup">没有账号,立即注册</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-alert
|
<el-alert v-if="msg" :title="msg" show-icon type="error" />
|
||||||
v-if="errorMessage"
|
|
||||||
:title="errorMessage"
|
|
||||||
show-icon
|
|
||||||
type="error"
|
|
||||||
/>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -196,8 +196,6 @@ export const LANGUAGE_LABEL = {
|
|||||||
Golang: "Go",
|
Golang: "Go",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LANGUAGE = keyof typeof LANGUAGE_LABEL
|
|
||||||
|
|
||||||
export const LANGUAGE_VALUE = {
|
export const LANGUAGE_VALUE = {
|
||||||
C: "c",
|
C: "c",
|
||||||
"C++": "cpp",
|
"C++": "cpp",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useDateFormat } from "@vueuse/core"
|
||||||
import { STORAGE_KEY } from "./constants"
|
import { STORAGE_KEY } from "./constants"
|
||||||
|
|
||||||
export function getACRate(acCount: number, totalCount: number) {
|
export function getACRate(acCount: number, totalCount: number) {
|
||||||
@@ -32,3 +33,8 @@ export function getTagColor(tag: string) {
|
|||||||
困难: "danger",
|
困难: "danger",
|
||||||
}[tag]
|
}[tag]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseTime(utc: Date, format = "YYYY年M月D日") {
|
||||||
|
const time = useDateFormat(utc, format, { locales: "zh-CN" })
|
||||||
|
return time.value
|
||||||
|
}
|
||||||
|
|||||||
51
src/utils/types.ts
Normal file
51
src/utils/types.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
export type LANGUAGE =
|
||||||
|
| "C"
|
||||||
|
| "C++"
|
||||||
|
| "Python2"
|
||||||
|
| "Python3"
|
||||||
|
| "Java"
|
||||||
|
| "JavaScript"
|
||||||
|
| "Golang"
|
||||||
|
|
||||||
|
export interface Problem {
|
||||||
|
_id: string
|
||||||
|
id: number
|
||||||
|
tags: string[]
|
||||||
|
created_by: {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
real_name: null
|
||||||
|
}
|
||||||
|
template: { [key in LANGUAGE]?: string }
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
input_description: string
|
||||||
|
output_description: string
|
||||||
|
samples: {
|
||||||
|
input: string
|
||||||
|
output: string
|
||||||
|
}[]
|
||||||
|
hint: string
|
||||||
|
languages: Array<LANGUAGE>
|
||||||
|
create_time: Date
|
||||||
|
last_update_time: null
|
||||||
|
time_limit: number
|
||||||
|
memory_limit: number
|
||||||
|
io_mode: {
|
||||||
|
input: string
|
||||||
|
output: string
|
||||||
|
io_mode: string
|
||||||
|
}
|
||||||
|
spj: boolean
|
||||||
|
spj_language: null
|
||||||
|
rule_type: string
|
||||||
|
difficulty: "Low" | "Mid" | "High"
|
||||||
|
source: string
|
||||||
|
total_score: number
|
||||||
|
submission_number: number
|
||||||
|
accepted_number: number
|
||||||
|
statistic_info: {}
|
||||||
|
share_submission: boolean
|
||||||
|
contest: null
|
||||||
|
my_status: number
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user