add charts.
This commit is contained in:
46
package-lock.json
generated
46
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@monaco-editor/loader": "^1.3.2",
|
||||
"@vueuse/core": "^9.11.1",
|
||||
"axios": "1.2.3",
|
||||
"chart.js": "^4.2.0",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
@@ -19,6 +20,7 @@
|
||||
"party-js": "^2.2.0",
|
||||
"pinia": "^2.0.29",
|
||||
"vue": "^3.2.45",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -485,6 +487,11 @@
|
||||
"resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"node_modules/@mdit-vue/plugin-component": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmmirror.com/@mdit-vue/plugin-component/-/plugin-component-0.11.2.tgz",
|
||||
@@ -1008,6 +1015,17 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz",
|
||||
@@ -2491,6 +2509,15 @@
|
||||
"@vue/shared": "3.2.45"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-chartjs": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-chartjs/-/vue-chartjs-5.2.0.tgz",
|
||||
"integrity": "sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^4.1.1",
|
||||
"vue": "^3.0.0-0 || ^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",
|
||||
@@ -2824,6 +2851,11 @@
|
||||
"resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
||||
},
|
||||
"@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"@mdit-vue/plugin-component": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmmirror.com/@mdit-vue/plugin-component/-/plugin-component-0.11.2.tgz",
|
||||
@@ -3275,6 +3307,14 @@
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"requires": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz",
|
||||
@@ -4375,6 +4415,12 @@
|
||||
"@vue/shared": "3.2.45"
|
||||
}
|
||||
},
|
||||
"vue-chartjs": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-chartjs/-/vue-chartjs-5.2.0.tgz",
|
||||
"integrity": "sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==",
|
||||
"requires": {}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@monaco-editor/loader": "^1.3.2",
|
||||
"@vueuse/core": "^9.11.1",
|
||||
"axios": "1.2.3",
|
||||
"chart.js": "^4.2.0",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"date-fns": "^2.29.3",
|
||||
"highlight.js": "^11.7.0",
|
||||
@@ -21,6 +22,7 @@
|
||||
"party-js": "^2.2.0",
|
||||
"pinia": "^2.0.29",
|
||||
"vue": "^3.2.45",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,32 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import Monaco from "../shared/Monaco.vue"
|
||||
import raw from "./step-1/1.c?raw"
|
||||
|
||||
import Monaco from "~/shared/Monaco.vue"
|
||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||
const route = useRoute()
|
||||
const id = route.hash.replace("#step-", "") || "1"
|
||||
const router = useRouter()
|
||||
|
||||
const Md = defineAsyncComponent(() => import(`./step-${id}/index.md`))
|
||||
const TOTAL = 3
|
||||
|
||||
const code = ref(raw)
|
||||
const Mds = Array.from({ length: TOTAL }, (_, i) => i + 1).map((v) =>
|
||||
defineAsyncComponent(() => import(`./step-${v}/index.md`))
|
||||
)
|
||||
const code = ref("")
|
||||
const step = computed(() => {
|
||||
if (!route.params.step || !route.params.step.length) return 1
|
||||
else {
|
||||
return parseInt(route.params.step[0].split("-")[1])
|
||||
}
|
||||
})
|
||||
|
||||
function change(value: string) {
|
||||
code.value = value
|
||||
watch(
|
||||
() => route.params.step,
|
||||
async (value) => {
|
||||
if (route.name !== "learn") return
|
||||
try {
|
||||
const raw = await import(`./${value[0]}/main.c?raw`)
|
||||
code.value = raw.default
|
||||
} catch (err) {
|
||||
router.replace("/learn/step-1")
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function change(v: string) {
|
||||
code.value = v
|
||||
}
|
||||
function prev() {
|
||||
router.push(`/learn/step-${step.value - 1}`)
|
||||
}
|
||||
function next() {
|
||||
router.push(`/learn/step-${step.value + 1}`)
|
||||
}
|
||||
function run() {
|
||||
console.log(code.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-grid :cols="2">
|
||||
<n-gi>
|
||||
<Md />
|
||||
<n-space justify="space-between">
|
||||
<n-button text type="primary">上一步</n-button>
|
||||
<n-button text type="primary">下一步</n-button>
|
||||
<n-grid v-if="isDesktop" :cols="24">
|
||||
<n-gi :span="10">
|
||||
<n-scrollbar style="max-height: calc(100vh - 92px)">
|
||||
<component :is="Mds[step - 1]"></component>
|
||||
<n-space justify="space-around">
|
||||
<n-button v-if="step !== 1" text type="primary" @click="prev">
|
||||
上一步
|
||||
</n-button>
|
||||
<n-button v-if="step !== TOTAL" text type="primary" @click="next">
|
||||
下一步
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-scrollbar>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-gi :span="14" class="relative">
|
||||
<n-button type="primary" class="action" @click="run">运行</n-button>
|
||||
<Monaco :value="code" @change="change" />
|
||||
<div></div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<div v-else></div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@@ -64,4 +104,15 @@ function change(value: string) {
|
||||
text-align: right;
|
||||
color: rgba(115, 138, 148, 0.4);
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,3 +13,99 @@ int main() {
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
# 11
|
||||
|
||||
## 1
|
||||
|
||||
哈哈
|
||||
|
||||
```c {3-4}
|
||||
#include<stdio.h>
|
||||
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
123
|
||||
|
||||
1
src/learn/step-2/index.md
Normal file
1
src/learn/step-2/index.md
Normal file
@@ -0,0 +1 @@
|
||||
asas
|
||||
1
src/learn/step-2/main.c
Normal file
1
src/learn/step-2/main.c
Normal file
@@ -0,0 +1 @@
|
||||
1212121212
|
||||
0
src/learn/step-3/index.md
Normal file
0
src/learn/step-3/index.md
Normal file
0
src/learn/step-3/main.c
Normal file
0
src/learn/step-3/main.c
Normal file
28
src/main.ts
28
src/main.ts
@@ -8,7 +8,19 @@ import { routes } from "./routes"
|
||||
import App from "./App.vue"
|
||||
|
||||
import { toggleLogin } from "./shared/composables/modal"
|
||||
import { init } from "./shared/composables/monaco"
|
||||
import { init as monacoInit } from "./shared/composables/monaco"
|
||||
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Colors,
|
||||
Tooltip,
|
||||
Legend,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
} from "chart.js"
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
@@ -26,7 +38,19 @@ router.beforeEach((to, from, next) => {
|
||||
next()
|
||||
}
|
||||
})
|
||||
init()
|
||||
|
||||
monacoInit()
|
||||
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
Colors,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend
|
||||
)
|
||||
const pinia = createPinia()
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { DIFFICULTY } from "utils/constants"
|
||||
import { Pie } from "vue-chartjs"
|
||||
import { DIFFICULTY, JUDGE_STATUS } from "utils/constants"
|
||||
import { getACRate, getTagColor, parseTime } from "utils/functions"
|
||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||
import { Problem } from "utils/types"
|
||||
@@ -7,7 +8,30 @@ import { Problem } from "utils/types"
|
||||
interface Props {
|
||||
problem: Problem
|
||||
}
|
||||
defineProps<Props>()
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const data = computed(() => {
|
||||
const status = props.problem.statistic_info
|
||||
const labels = []
|
||||
for (let i in status) {
|
||||
if (status[i] === 0) {
|
||||
delete status[i]
|
||||
}
|
||||
// @ts-ignore
|
||||
labels.push(JUDGE_STATUS[i]["name"])
|
||||
}
|
||||
return {
|
||||
labels,
|
||||
datasets: [{ data: Object.values(status), hoverOffset: 4 }],
|
||||
}
|
||||
})
|
||||
|
||||
const options = ref({
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: { text: "提交结果的比例", display: true, font: { size: 20 } },
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,4 +73,14 @@ defineProps<Props>()
|
||||
</n-space>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
<div class="pie">
|
||||
<Pie :data="data" :options="options" />
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.pie {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
88
src/oj/rank/components/Chart.vue
Normal file
88
src/oj/rank/components/Chart.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import { Bar } from "vue-chartjs"
|
||||
import { Rank } from "~/utils/types"
|
||||
|
||||
const props = defineProps<{ rankData: Rank[] }>()
|
||||
|
||||
const data = computed(() => ({
|
||||
labels: props.rankData.map((rank) => rank.user.username),
|
||||
datasets: [
|
||||
{
|
||||
label: "已解决",
|
||||
data: props.rankData.map((rank) => rank.accepted_number),
|
||||
backgroundColor: [
|
||||
"rgba(255, 99, 132, 0.2)",
|
||||
"rgba(255, 159, 64, 0.2)",
|
||||
"rgba(55, 66, 250, 0.2)",
|
||||
"rgba(75, 192, 192, 0.2)",
|
||||
"rgba(54, 162, 235, 0.2)",
|
||||
"rgba(153, 102, 255, 0.2)",
|
||||
"rgba(48, 51, 107, 0.2)",
|
||||
"rgba(249, 202, 36, 0.2)",
|
||||
"rgba(106, 176, 76, 0.2)",
|
||||
"rgba(119, 139, 235, 0.2)",
|
||||
],
|
||||
borderColor: [
|
||||
"rgba(255, 99, 132, 0.6)",
|
||||
"rgba(255, 159, 64, 0.6)",
|
||||
"rgba(55, 66, 250, 0.6)",
|
||||
"rgba(75, 192, 192, 0.6)",
|
||||
"rgba(54, 162, 235, 0.6)",
|
||||
"rgba(153, 102, 255, 0.6)",
|
||||
"rgba(48, 51, 107, 0.6)",
|
||||
"rgba(249, 202, 36, 0.6)",
|
||||
"rgba(106, 176, 76, 0.6)",
|
||||
"rgba(119, 139, 235, 0.6)",
|
||||
],
|
||||
hoverBackgroundColor: [
|
||||
"rgba(255, 99, 132, 0.8)",
|
||||
"rgba(255, 159, 64, 0.8)",
|
||||
"rgba(55, 66, 250, 0.8)",
|
||||
"rgba(75, 192, 192, 0.8)",
|
||||
"rgba(54, 162, 235, 0.8)",
|
||||
"rgba(153, 102, 255, 0.8)",
|
||||
"rgba(48, 51, 107, 0.8)",
|
||||
"rgba(249, 202, 36, 0.8)",
|
||||
"rgba(106, 176, 76, 0.8)",
|
||||
"rgba(119, 139, 235, 0.8)",
|
||||
],
|
||||
hoverBorderColor: [
|
||||
"rgba(255, 99, 132, 1)",
|
||||
"rgba(255, 159, 64, 1)",
|
||||
"rgba(55, 66, 250, 1)",
|
||||
"rgba(75, 192, 192, 1)",
|
||||
"rgba(54, 162, 235, 1)",
|
||||
"rgba(153, 102, 255, 1)",
|
||||
"rgba(48, 51, 107, 1)",
|
||||
"rgba(249, 202, 36, 1)",
|
||||
"rgba(106, 176, 76, 1)",
|
||||
"rgba(119, 139, 235, 1)",
|
||||
],
|
||||
borderWidth: 2,
|
||||
},
|
||||
{
|
||||
label: "总提交数",
|
||||
data: props.rankData.map((rank) => rank.submission_number),
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
const options = ref({
|
||||
plugins: {
|
||||
title: {
|
||||
text: "全校前十名的提交者(不包括超管)",
|
||||
display: true,
|
||||
font: { size: 20 },
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<Bar class="chart" :data="data" :options="options" />
|
||||
</template>
|
||||
<style scoped>
|
||||
.chart {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { DataTableColumn, NButton, NButtonGroup } from "naive-ui"
|
||||
import { DataTableColumn, NButton } from "naive-ui"
|
||||
import Chart from "./components/Chart.vue"
|
||||
import Pagination from "~/shared/Pagination.vue"
|
||||
import { Rank } from "utils/types"
|
||||
import { getRank } from "oj/api"
|
||||
@@ -11,13 +12,18 @@ const query = reactive({
|
||||
limit: 10,
|
||||
page: 1,
|
||||
})
|
||||
const rankData = ref<Rank[]>([])
|
||||
|
||||
async function listRanks() {
|
||||
const offset = (query.page - 1) * query.limit
|
||||
const res = await getRank(offset, query.limit)
|
||||
data.value = res.data.results
|
||||
total.value = res.data.total
|
||||
if (query.page === 1) {
|
||||
rankData.value = data.value
|
||||
}
|
||||
}
|
||||
|
||||
const columns: DataTableColumn<Rank>[] = [
|
||||
{
|
||||
title: "排名",
|
||||
@@ -61,6 +67,7 @@ onMounted(listRanks)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Chart v-if="!!rankData.length" :rankData="rankData" />
|
||||
<n-data-table striped size="small" :data="data" :columns="columns" />
|
||||
<Pagination
|
||||
:total="total"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export const routes = [
|
||||
import { RouteRecordRaw } from "vue-router"
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("~/shared/layout/default.vue"),
|
||||
@@ -41,7 +43,12 @@ export const routes = [
|
||||
},
|
||||
{
|
||||
path: "learn",
|
||||
redirect: "learn/step-1",
|
||||
},
|
||||
{
|
||||
path: "learn/:step+",
|
||||
component: () => import("learn/index.vue"),
|
||||
name: "learn",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ const defaultValue = computed(() => route.path.split("/")[1] || "problem")
|
||||
const menus: MenuOption[] = [
|
||||
{
|
||||
label: () =>
|
||||
h(RouterLink, { to: "/learn#step-1" }, { default: () => "自学" }),
|
||||
h(RouterLink, { to: "/learn/step-1" }, { default: () => "自学" }),
|
||||
key: "learn",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -96,7 +96,7 @@ export interface Problem {
|
||||
total_score: number
|
||||
submission_number: number
|
||||
accepted_number: number
|
||||
statistic_info: {}
|
||||
statistic_info: { [key in string]: number }
|
||||
share_submission: boolean
|
||||
contest: null
|
||||
my_status: number
|
||||
|
||||
Reference in New Issue
Block a user