add user homepage.
This commit is contained in:
48
package-lock.json
generated
48
package-lock.json
generated
@@ -11,8 +11,8 @@
|
||||
"@element-plus/icons-vue": "^2.0.10",
|
||||
"@monaco-editor/loader": "^1.3.2",
|
||||
"@vueuse/core": "^9.12.0",
|
||||
"axios": "1.3.2",
|
||||
"chart.js": "^4.2.0",
|
||||
"axios": "1.3.3",
|
||||
"chart.js": "^4.2.1",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
@@ -24,7 +24,7 @@
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ep": "^1.1.8",
|
||||
"@iconify-json/ep": "^1.1.9",
|
||||
"@types/node": "^18.13.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"markdown-it-shiki": "^0.7.2",
|
||||
@@ -32,7 +32,7 @@
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "^4.9.5",
|
||||
"unplugin-auto-import": "^0.14.2",
|
||||
"unplugin-icons": "^0.15.2",
|
||||
"unplugin-icons": "^0.15.3",
|
||||
"unplugin-vue-components": "^0.23.0",
|
||||
"vite": "^4.1.1",
|
||||
"vite-plugin-vue-markdown": "^0.22.4",
|
||||
@@ -790,9 +790,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify-json/ep": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/@iconify-json/ep/-/ep-1.1.8.tgz",
|
||||
"integrity": "sha512-pHCrsWU1R9/pTDU+Fps4+mjqOQFLtpGdXWegkhQ1P1DlgQAlCPyICtl6E1s8b7VwJMeZXaK84HA02UF6WD0o/Q==",
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmmirror.com/@iconify-json/ep/-/ep-1.1.9.tgz",
|
||||
"integrity": "sha512-vhrCvikS/uRsEaM8eMyH7Fj13TSbkOuXqn0W/hqj79C9mtlUZMXPq31f+Mr5Cw4ag6sBPt8uY5WMhbwAR0vOMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@iconify/types": "*"
|
||||
@@ -805,17 +805,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@iconify/utils": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.1.1.tgz",
|
||||
"integrity": "sha512-H8xz74JDzDw8f0qLxwIaxFMnFkbXTZNWEufOk3WxaLFHV4h0A2FjIDgNk5LzC0am4jssnjdeJJdRs3UFu3582Q==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.1.3.tgz",
|
||||
"integrity": "sha512-4rnzpZ2AWztPKDyWtw+DwJ9uko24it6YS+cnVpZveOrvLErwg22eXcGnIfuMFyECvsfbFhMqZW5YYWHe3CyEEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@antfu/install-pkg": "^0.1.1",
|
||||
"@antfu/utils": "^0.7.2",
|
||||
"@iconify/types": "^2.0.0",
|
||||
"debug": "^4.3.4",
|
||||
"kolorist": "^1.6.0",
|
||||
"local-pkg": "^0.4.2"
|
||||
"kolorist": "^1.7.0",
|
||||
"local-pkg": "^0.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
@@ -1442,9 +1442,9 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.3.2.tgz",
|
||||
"integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.3.3.tgz",
|
||||
"integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -1551,9 +1551,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.2.0.tgz",
|
||||
"integrity": "sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.2.1.tgz",
|
||||
"integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
@@ -3485,17 +3485,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-icons": {
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.2.tgz",
|
||||
"integrity": "sha512-oWTTdLMuqfEYfZcko+KZHDEOIsqT4OeyJB1e4U7luCOo9gto/JLyHkqfbqjmjkjdQqA3DNHS18WOKh5esqQM5g==",
|
||||
"version": "0.15.3",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.3.tgz",
|
||||
"integrity": "sha512-YWgJqv5AahrokeOnta8uX/m1damZA6Rf6zPClgHg2Fa/45iyOe3Lj+Wn/Ba+CSsq9yBffn17YfKfJNyWCNZPvw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@antfu/install-pkg": "^0.1.1",
|
||||
"@antfu/utils": "^0.7.2",
|
||||
"@iconify/utils": "^2.1.0",
|
||||
"@iconify/utils": "^2.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"kolorist": "^1.6.0",
|
||||
"local-pkg": "^0.4.2",
|
||||
"kolorist": "^1.7.0",
|
||||
"local-pkg": "^0.4.3",
|
||||
"unplugin": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"@element-plus/icons-vue": "^2.0.10",
|
||||
"@monaco-editor/loader": "^1.3.2",
|
||||
"@vueuse/core": "^9.12.0",
|
||||
"axios": "1.3.2",
|
||||
"chart.js": "^4.2.0",
|
||||
"axios": "1.3.3",
|
||||
"chart.js": "^4.2.1",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
@@ -26,7 +26,7 @@
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ep": "^1.1.8",
|
||||
"@iconify-json/ep": "^1.1.9",
|
||||
"@types/node": "^18.13.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"markdown-it-shiki": "^0.7.2",
|
||||
@@ -34,7 +34,7 @@
|
||||
"prettier": "^2.8.4",
|
||||
"typescript": "^4.9.5",
|
||||
"unplugin-auto-import": "^0.14.2",
|
||||
"unplugin-icons": "^0.15.2",
|
||||
"unplugin-icons": "^0.15.3",
|
||||
"unplugin-vue-components": "^0.23.0",
|
||||
"vite": "^4.1.1",
|
||||
"vite-plugin-vue-markdown": "^0.22.4",
|
||||
|
||||
5
src/components.d.ts
vendored
5
src/components.d.ts
vendored
@@ -16,20 +16,23 @@ declare module '@vue/runtime-core' {
|
||||
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
||||
IEpSunny: typeof import('~icons/ep/sunny')['default']
|
||||
NAlert: typeof import('naive-ui')['NAlert']
|
||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCode: typeof import('naive-ui')['NCode']
|
||||
NCode: typeof import("naive-ui")["NCode"]
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NCountdown: typeof import("naive-ui")["NCountdown"]
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NImage: typeof import("naive-ui")["NImage"]
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NLayout: typeof import('naive-ui')['NLayout']
|
||||
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
||||
|
||||
@@ -40,7 +40,15 @@ const columns = ref<DataTableColumn<ContestRank>[]>([
|
||||
fixed: "left",
|
||||
align: "center",
|
||||
render: (row) =>
|
||||
h(NButton, { text: true, type: "info" }, () => row.user.username),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: "info",
|
||||
onClick: () => router.push("/user?name=" + row.user.username),
|
||||
},
|
||||
() => row.user.username
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "正确数/总提交",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Rank } from "utils/types"
|
||||
import { getRank } from "oj/api"
|
||||
import { getACRate } from "utils/functions"
|
||||
|
||||
const router = useRouter()
|
||||
const data = ref<Rank[]>([])
|
||||
const total = ref(0)
|
||||
const query = reactive({
|
||||
@@ -39,7 +40,11 @@ const columns: DataTableColumn<Rank>[] = [
|
||||
render: (row) =>
|
||||
h(
|
||||
NButton,
|
||||
{ text: true, type: "info", onClick: () => {} },
|
||||
{
|
||||
text: true,
|
||||
type: "info",
|
||||
onClick: () => router.push("/user?name=" + row.user.username),
|
||||
},
|
||||
() => row.user.username
|
||||
),
|
||||
},
|
||||
|
||||
@@ -61,7 +61,7 @@ onMounted(init)
|
||||
>
|
||||
<n-space>
|
||||
<span>提交时间:{{ parseTime(submission.create_time) }}</span>
|
||||
<span>语言:{{ submission.language }}</span>
|
||||
<span>编程语言:{{ submission.language }}</span>
|
||||
<span>用户:{{ submission.username }}</span>
|
||||
</n-space>
|
||||
</n-alert>
|
||||
|
||||
@@ -196,7 +196,15 @@ const columns = computed(() => {
|
||||
key: "username",
|
||||
minWidth: 120,
|
||||
render: (row) =>
|
||||
h(NButton, { text: true, type: "info" }, () => row.username),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: "info",
|
||||
onClick: () => router.push("/user?name=" + row.username),
|
||||
},
|
||||
() => row.username
|
||||
),
|
||||
},
|
||||
]
|
||||
if (!route.params.contestID && userStore.isSuperAdmin) {
|
||||
|
||||
@@ -1,3 +1,84 @@
|
||||
<script setup lang="ts"></script>
|
||||
<template></template>
|
||||
<style scoped></style>
|
||||
<script setup lang="ts">
|
||||
import { getProfile } from "~/shared/api"
|
||||
import { Profile } from "~/utils/types"
|
||||
|
||||
const route = useRoute()
|
||||
const profile = ref<Profile | null>(null)
|
||||
const problems = ref<string[]>([])
|
||||
const [loading, toggle] = useToggle()
|
||||
|
||||
async function init() {
|
||||
toggle(true)
|
||||
try {
|
||||
const res = await getProfile(route.query.name as string)
|
||||
profile.value = res.data
|
||||
const acm = res.data.acm_problems_status.problems || {}
|
||||
const oi = res.data.oi_problems_status.problems || {}
|
||||
const ac: string[] = []
|
||||
for (let problems of [acm, oi]) {
|
||||
Object.keys(problems).forEach((problemID) => {
|
||||
if (problems[problemID]["status"] === 0) {
|
||||
ac.push(problems[problemID]["_id"])
|
||||
}
|
||||
})
|
||||
}
|
||||
ac.sort()
|
||||
problems.value = ac
|
||||
} finally {
|
||||
toggle(false)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(init)
|
||||
</script>
|
||||
<template>
|
||||
<n-space vertical justify="center" align="center" v-if="!loading && profile">
|
||||
<n-avatar round :size="140" :src="profile.avatar" />
|
||||
<h2>{{ profile.user.username }}</h2>
|
||||
<p class="desc">{{ profile.mood }}</p>
|
||||
</n-space>
|
||||
<n-descriptions
|
||||
v-if="!loading && profile"
|
||||
class="wrapper"
|
||||
bordered
|
||||
:column="2"
|
||||
label-style="width: 50%"
|
||||
>
|
||||
<n-descriptions-item label="已解决的问题数量">
|
||||
{{ profile.accepted_number }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="总提交数">
|
||||
{{ profile.submission_number }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item v-if="problems.length" label="已解决的问题" :span="2">
|
||||
<n-space>
|
||||
<n-button
|
||||
v-for="id in problems"
|
||||
key="id"
|
||||
@click="$router.push('/problem/' + id)"
|
||||
>
|
||||
{{ id }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
<n-empty v-if="!loading && !profile" description="该用户不存在">
|
||||
<template #extra>
|
||||
<n-button @click="$router.push('/')">返回主页</n-button>
|
||||
</template>
|
||||
</n-empty>
|
||||
</template>
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
max-width: 600px;
|
||||
margin: 16px auto 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user