@xcpcio/board-app 0.23.0 → 0.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/about.html +2 -2
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-bdd603c5.js +1 -0
- package/dist/assets/TheInput.vue_vue_type_script_setup_true_lang-19d39372.js +1 -0
- package/dist/assets/{_...all_-bca664b6.css → _...all_-65bba7d0.css} +1 -1
- package/dist/assets/_...all_-76798627.js +3 -0
- package/dist/assets/_name_-82a33e88.js +1 -0
- package/dist/assets/{about-caa3031e.js → about-6cc32222.js} +2 -2
- package/dist/assets/{app-dacf61f3.js → app-a6c41ce5.js} +52 -52
- package/dist/assets/{board-layout-127eb0a9.js → board-layout-a5587489.js} +1 -1
- package/dist/assets/headless-13d3a223.js +1 -0
- package/dist/assets/{home-c816ad74.js → home-6560de4b.js} +1 -1
- package/dist/assets/index-4ddb0930.js +1 -0
- package/dist/assets/{index-517541e1.css → index-da8e69bb.css} +2 -2
- package/dist/assets/{index-layout-650bba92.js → index-layout-f4d3ebfe.js} +1 -1
- package/dist/assets/test-4090a9c4.js +1 -0
- package/dist/assets/{user-5e7a1733.js → user-5e01721a.js} +1 -1
- package/dist/assets/{virtual_pwa-register-54c5c068.js → virtual_pwa-register-af30bbb9.js} +1 -1
- package/dist/balloon.html +1 -0
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +1 -1
- package/dist/ssr-manifest.json +181 -163
- package/dist/sw.js +1 -1
- package/dist/test.html +1 -1
- package/package.json +3 -3
- package/src/auto-imports.d.ts +12 -0
- package/src/components/Balloon.vue +99 -0
- package/src/components/BalloonBlock.vue +85 -0
- package/src/components/{ContestIndex.vue → ContestIndexUI.vue} +3 -3
- package/src/components/CustomBalloon.vue +25 -0
- package/src/components/CustomBoard.vue +15 -0
- package/src/components/{board/CustomBoard.vue → DataSourceInput.vue} +8 -20
- package/src/components/board/Board.vue +3 -2
- package/src/components/board/TeamUI.vue +4 -1
- package/src/components/board/Utility.vue +23 -1
- package/src/components.d.ts +6 -3
- package/src/composables/constant.ts +3 -0
- package/src/composables/query.ts +9 -0
- package/src/layouts/headless.vue +7 -0
- package/src/pages/balloon/index.vue +10 -0
- package/src/pages/index.vue +4 -1
- package/src/styles/color.css +8 -0
- package/src/styles/main.css +1 -0
- package/src/styles/resolver.css +8 -0
- package/uno.config.ts +11 -0
- package/dist/assets/TheInput.vue_vue_type_script_setup_true_lang-7e223c40.js +0 -1
- package/dist/assets/_...all_-f4969270.js +0 -3
- package/dist/assets/_name_-8f131dce.js +0 -1
- package/dist/assets/test-a2803908.js +0 -1
- package/src/components/board/Balloon.vue +0 -5
- /package/src/{components → composables}/logo/ccpc.ts +0 -0
- /package/src/{components → composables}/logo/hunan-cpc.ts +0 -0
- /package/src/{components → composables}/logo/icpc.ts +0 -0
- /package/src/{components → composables}/logo/index.ts +0 -0
package/dist/test.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-critters-container><head><meta charset="UTF-8"><!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> --><link rel="apple-touch-icon" href="https://cdn.jsdelivr.net/npm/@xcpcio/board-app@0.
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-critters-container><head><meta charset="UTF-8"><!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> --><link rel="apple-touch-icon" href="https://cdn.jsdelivr.net/npm/@xcpcio/board-app@0.24.3/dist/balloon-192x192.png"><link rel="mask-icon" href="https://cdn.jsdelivr.net/npm/@xcpcio/board-app@0.24.3/dist/balloon-512x512.svg" color="#00aba9"><meta name="msapplication-TileColor" content="#00aba9"><script>function normalizePath(_){for(;_.endsWith("/");)_=_.slice(0,-1);return _+"/"}{let _="__CDN_HOST__";_=normalizePath(_),window.CDN_HOST=_}try{let _=__DATA_HOST__;_=normalizePath(_),window.DATA_HOST=_}catch(_){window.DATA_HOST="/data/"}try{let _=__DATA_REGION__;window.DATA_REGION=_}catch(_){window.DATA_REGION="CN"}</script><script>!function(){var e=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches,t=localStorage.getItem("vueuse-color-scheme")||"auto";("dark"===t||e&&"light"!==t)&&document.documentElement.classList.toggle("dark",!0)}()</script><script type="module" async crossorigin src="https://cdn.jsdelivr.net/npm/@xcpcio/board-app@0.24.3/dist/assets/app-a6c41ce5.js"></script><link rel="manifest" href="/manifest.webmanifest"><style></style><link rel="modulepreload" crossorigin href="/assets/home-6560de4b.js"><link rel="modulepreload" crossorigin href="/assets/test-4090a9c4.js"><title>Board - XCPCIO</title><meta name="description" content="XCPCIO-Board 主要收录 *CPC 系列竞赛的榜单。"><meta name="keywords" content="icpc, ccpc, rank, board, standings"><meta name="theme-color" content="#ffffff"><link rel="icon" type="image/svg+xml" href="/favicon.svg"></head><body class="font-sans"><div id="app" data-server-rendered="true"><main px-4 py-10 text="center gray-700 dark:gray-200"><div flex flex-col justify-center items-center><div text-4xl><div i-carbon-campsite></div></div><p><a rel="noreferrer" href="https://github.com/antfu/vitesse" target="_blank">Vitesse</a></p><p><em text-sm opacity-75>Opinionated Vite Starter Template</em></p><div py-4></div><div w-48><input id="input" value type="text" placeholder="What's your name?" autocomplete="false" p="x-4 y-2" w-full text="center" bg="transparent" border="~ rounded gray-200 dark:gray-700" outline="none active:none"><label class="hidden" for="input">What's your name?</label></div><div><button m-3 text-sm btn disabled="disabled">GO</button></div></div><footer mt-8 py-4 md:py-8 lg:py-10 w-full flex justify-center items-center><div text-center w-full><a href="/" class flex items-center justify-center text-2xl font-semibold text-gray-900 dark:text-white><div i-ion-balloon-sharp mr-4 h-8></div>Get more balloons</a><p my-6 text-gray-500 dark:text-gray-400 w-full flex justify-center>Open-source project of ICPC/CCPC Standings.</p><ul mb-6 w-full flex flex-wrap items-center justify-center text-gray-900 dark:text-white><li><a href="/" class mr-4 md:mr-6 hover:underline>Home</a></li><li><div class="v-popper v-popper--theme-tooltip" w-inherit><!--[--><!--[--><a href="https://github.com/xcpcio/xcpcio" class="mr-4 md:mr-6 hover:underline" rel="noreferrer" target="_blank" title="GitHub">GitHub</a><!--]--><div id="popper_1vvkpjor_17h6rl" class="v-popper__popper v-popper--theme-tooltip v-popper__popper--hidden v-popper__popper--hide-to" style="position:absolute;transform:translate3d(0,0,0)" aria-hidden="true" data-popper-placement><div class="v-popper__backdrop"></div><div class="v-popper__wrapper" style><div class="v-popper__inner"><!----></div><div class="v-popper__arrow-container" style="left:0;top:0"><div class="v-popper__arrow-outer"></div><div class="v-popper__arrow-inner"></div></div></div></div><!--]--></div></li><li><div class="v-popper v-popper--theme-tooltip" w-inherit><!--[--><!--[--><a href="mailto:hi@dup4.com" mr-4 md:mr-6 hover:underline>Contact</a><!--]--><div id="popper_5she32xz_17h6rl" class="v-popper__popper v-popper--theme-tooltip v-popper__popper--hidden v-popper__popper--hide-to" style="position:absolute;transform:translate3d(0,0,0)" aria-hidden="true" data-popper-placement><div class="v-popper__backdrop"></div><div class="v-popper__wrapper" style><div class="v-popper__inner"><!----></div><div class="v-popper__arrow-container" style="left:0;top:0"><div class="v-popper__arrow-outer"></div><div class="v-popper__arrow-inner"></div></div></div></div><!--]--></div></li></ul><span text-sm text-gray-500 sm:text-center dark:text-gray-400 w-full>© 2020-PRESENT <a href="/" class hover:underline>XCPCIO™</a>. All Rights Reserved.</span></div></footer><div mx-auto mt-5 text-center text-sm opacity-50>[Home Layout]</div></main></div><script>window.__INITIAL_STATE__='{"pinia":{"user":{"savedName":""}}}'</script><noscript>This website requires JavaScript to function properly. Please enable JavaScript to continue.</noscript><script>try{var umamiJSUrl=__UMAMI_JS_URL__,umamiWebsiteId=__UMAMI_WEBSITE_ID__;!function(){var e=document.createElement("script"),t=(e.src=umamiJSUrl,e.defer=!0,e.setAttribute("data-website-id",umamiWebsiteId),document.getElementsByTagName("script")[0]);t.parentNode.insertBefore(e,t)}()}catch(e){}try{var plausibleJSUrl=__PLAUSIBLE_JS_URL__,plausibleDataDomain=__PLAUSIBLE_DATA_DOMAIN__;!function(){var e=document.createElement("script"),t=(e.src=plausibleJSUrl,e.defer=!0,e.setAttribute("data-domain",plausibleDataDomain),document.getElementsByTagName("script")[0]);t.parentNode.insertBefore(e,t)}()}catch(e){}try{var baiduAnalyticsId=__BAIDU_ANALYTICS_ID__,_hmt=_hmt||[];!function(){var e=document.createElement("script"),t=(e.src="https://hm.baidu.com/hm.js?"+baiduAnalyticsId,document.getElementsByTagName("script")[0]);t.parentNode.insertBefore(e,t)}()}catch(e){}try{var googleAnalyticsId=__GOOGLE_ANALYTICS_ID__;!function(){var e,t,a,r;location.port||(e=window,r=document,t="script",a="ga",e.GoogleAnalyticsObject=a,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=+new Date,a=r.createElement(t),r=r.getElementsByTagName(t)[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",r.parentNode.insertBefore(a,r),ga("create",""+googleAnalyticsId,"auto"),ga("send","pageview"))}()}catch(e){}</script></body></html>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcpcio/board-app",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.24.3",
|
|
5
5
|
"description": "XCPCIO Board App",
|
|
6
6
|
"author": "Dup4 <lyuzhi.pan@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"vue-router": "^4.2.4",
|
|
54
54
|
"vue-search-select": "^3.1.2",
|
|
55
55
|
"vue-toast-notification": "^3",
|
|
56
|
-
"@xcpcio/core": "0.
|
|
57
|
-
"@xcpcio/types": "0.
|
|
56
|
+
"@xcpcio/core": "0.24.3",
|
|
57
|
+
"@xcpcio/types": "0.24.3"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@antfu/eslint-config": "^0.39.8",
|
package/src/auto-imports.d.ts
CHANGED
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
// Generated by unplugin-auto-import
|
|
6
6
|
export {}
|
|
7
7
|
declare global {
|
|
8
|
+
const BALLOON_TITLE_SUFFIX: typeof import('./composables/constant')['BALLOON_TITLE_SUFFIX']
|
|
8
9
|
const EffectScope: typeof import('vue')['EffectScope']
|
|
9
10
|
const Pagination: typeof import('./composables/pagination')['Pagination']
|
|
11
|
+
const RESOLVER_TITLE_SUFFIX: typeof import('./composables/constant')['RESOLVER_TITLE_SUFFIX']
|
|
12
|
+
const TITLE_SUFFIX: typeof import('./composables/constant')['TITLE_SUFFIX']
|
|
10
13
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
|
11
14
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
|
12
15
|
const class_pagination_ix: typeof import('./composables/pagination')['class_pagination_ix']
|
|
@@ -40,6 +43,7 @@ declare global {
|
|
|
40
43
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
|
41
44
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
|
42
45
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
|
46
|
+
const getDataSourceUrl: typeof import('./composables/query')['getDataSourceUrl']
|
|
43
47
|
const getLocalStorageKeyForFilterOrganizations: typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterOrganizations']
|
|
44
48
|
const getLocalStorageKeyForFilterTeams: typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterTeams']
|
|
45
49
|
const getProblemChart: typeof import('./composables/statistics')['getProblemChart']
|
|
@@ -315,8 +319,11 @@ declare global {
|
|
|
315
319
|
import { UnwrapRef } from 'vue'
|
|
316
320
|
declare module 'vue' {
|
|
317
321
|
interface ComponentCustomProperties {
|
|
322
|
+
readonly BALLOON_TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['BALLOON_TITLE_SUFFIX']>
|
|
318
323
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
|
319
324
|
readonly Pagination: UnwrapRef<typeof import('./composables/pagination')['Pagination']>
|
|
325
|
+
readonly RESOLVER_TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['RESOLVER_TITLE_SUFFIX']>
|
|
326
|
+
readonly TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['TITLE_SUFFIX']>
|
|
320
327
|
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
|
321
328
|
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
|
322
329
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
|
@@ -345,6 +352,7 @@ declare module 'vue' {
|
|
|
345
352
|
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
|
346
353
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
|
347
354
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
|
355
|
+
readonly getDataSourceUrl: UnwrapRef<typeof import('./composables/query')['getDataSourceUrl']>
|
|
348
356
|
readonly getLocalStorageKeyForFilterOrganizations: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterOrganizations']>
|
|
349
357
|
readonly getLocalStorageKeyForFilterTeams: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterTeams']>
|
|
350
358
|
readonly getProblemChart: UnwrapRef<typeof import('./composables/statistics')['getProblemChart']>
|
|
@@ -611,8 +619,11 @@ declare module 'vue' {
|
|
|
611
619
|
}
|
|
612
620
|
declare module '@vue/runtime-core' {
|
|
613
621
|
interface ComponentCustomProperties {
|
|
622
|
+
readonly BALLOON_TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['BALLOON_TITLE_SUFFIX']>
|
|
614
623
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
|
615
624
|
readonly Pagination: UnwrapRef<typeof import('./composables/pagination')['Pagination']>
|
|
625
|
+
readonly RESOLVER_TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['RESOLVER_TITLE_SUFFIX']>
|
|
626
|
+
readonly TITLE_SUFFIX: UnwrapRef<typeof import('./composables/constant')['TITLE_SUFFIX']>
|
|
616
627
|
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
|
617
628
|
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
|
618
629
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
|
@@ -641,6 +652,7 @@ declare module '@vue/runtime-core' {
|
|
|
641
652
|
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
|
642
653
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
|
643
654
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
|
655
|
+
readonly getDataSourceUrl: UnwrapRef<typeof import('./composables/query')['getDataSourceUrl']>
|
|
644
656
|
readonly getLocalStorageKeyForFilterOrganizations: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterOrganizations']>
|
|
645
657
|
readonly getLocalStorageKeyForFilterTeams: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterTeams']>
|
|
646
658
|
readonly getProblemChart: UnwrapRef<typeof import('./composables/statistics')['getProblemChart']>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Rank, createContest, createSubmissions, createTeams } from "@xcpcio/core";
|
|
3
|
+
import type { Contest, Submissions, Teams } from "@xcpcio/core";
|
|
4
|
+
import type { Contest as IContest, Submissions as ISubmissions, Teams as ITeams } from "@xcpcio/types";
|
|
5
|
+
|
|
6
|
+
import { BALLOON_TITLE_SUFFIX } from "~/composables/constant";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
dataSourceUrl: string,
|
|
10
|
+
}>();
|
|
11
|
+
|
|
12
|
+
const title = useTitle(BALLOON_TITLE_SUFFIX);
|
|
13
|
+
const { t } = useI18n();
|
|
14
|
+
|
|
15
|
+
const firstLoaded = ref(false);
|
|
16
|
+
const contestData = ref({} as Contest);
|
|
17
|
+
const teamsData = ref([] as Teams);
|
|
18
|
+
const submissionsData = ref([] as Submissions);
|
|
19
|
+
const rank = ref({} as Rank);
|
|
20
|
+
const now = ref(new Date());
|
|
21
|
+
|
|
22
|
+
function reBuildBalloons() {
|
|
23
|
+
const newRank = new Rank(contestData.value, teamsData.value, submissionsData.value);
|
|
24
|
+
newRank.buildBalloons();
|
|
25
|
+
rank.value = newRank;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { data, isError, error } = useQueryBoardData(props.dataSourceUrl, now);
|
|
29
|
+
watch(data, async () => {
|
|
30
|
+
if (data.value === null || data.value === undefined) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
contestData.value = createContest(data.value?.contest as IContest);
|
|
35
|
+
title.value = `${contestData.value.name} | ${BALLOON_TITLE_SUFFIX}`;
|
|
36
|
+
|
|
37
|
+
teamsData.value = createTeams(data.value?.teams as ITeams);
|
|
38
|
+
submissionsData.value = createSubmissions(data.value?.submissions as ISubmissions);
|
|
39
|
+
|
|
40
|
+
reBuildBalloons();
|
|
41
|
+
|
|
42
|
+
firstLoaded.value = true;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const balloons = computed(() => {
|
|
46
|
+
return rank.value.balloons;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const setNowIntervalId = setInterval(() => {
|
|
50
|
+
now.value = new Date();
|
|
51
|
+
}, 1000);
|
|
52
|
+
|
|
53
|
+
onUnmounted(() => {
|
|
54
|
+
clearInterval(setNowIntervalId);
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<div
|
|
60
|
+
class="bg-[#323443]"
|
|
61
|
+
text-gray-200
|
|
62
|
+
>
|
|
63
|
+
<div v-if="!firstLoaded">
|
|
64
|
+
<div
|
|
65
|
+
flex flex-col
|
|
66
|
+
justify-center items-center
|
|
67
|
+
w-screen
|
|
68
|
+
h-screen
|
|
69
|
+
text-xl italic
|
|
70
|
+
>
|
|
71
|
+
<div>
|
|
72
|
+
{{ t("common.loading") }}...
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div v-if="isError">
|
|
76
|
+
{{ error }}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div
|
|
82
|
+
v-else
|
|
83
|
+
>
|
|
84
|
+
<div
|
|
85
|
+
flex flex-col justify-between
|
|
86
|
+
>
|
|
87
|
+
<template
|
|
88
|
+
v-for="(b, ix) in balloons"
|
|
89
|
+
:key="b.key"
|
|
90
|
+
>
|
|
91
|
+
<BalloonBlock
|
|
92
|
+
:index="ix"
|
|
93
|
+
:balloon="b"
|
|
94
|
+
/>
|
|
95
|
+
</template>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</template>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Balloon, Team } from "@xcpcio/core";
|
|
3
|
+
import type { ThemeColor } from "@xcpcio/types";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
index: number;
|
|
7
|
+
balloon: Balloon,
|
|
8
|
+
}>();
|
|
9
|
+
|
|
10
|
+
const balloon = computed(() => {
|
|
11
|
+
return props.balloon;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const el = ref(null);
|
|
15
|
+
const isVisible = useElementVisibility(el);
|
|
16
|
+
|
|
17
|
+
function showTeamName(team: Team) {
|
|
18
|
+
const sections = [team.location, team.organization, team.name];
|
|
19
|
+
return sections.filter(s => s).join(" - ");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getColor(c: ThemeColor): string {
|
|
23
|
+
if (typeof c === "string") {
|
|
24
|
+
return c;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (isDark && c?.dark) {
|
|
28
|
+
return c.dark;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return c.light;
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div
|
|
37
|
+
ref="el"
|
|
38
|
+
h-24
|
|
39
|
+
>
|
|
40
|
+
<div
|
|
41
|
+
v-if="isVisible"
|
|
42
|
+
h-24
|
|
43
|
+
flex flex-row gap-x-4
|
|
44
|
+
font-mono text-4xl
|
|
45
|
+
:class="[props.index % 2 === 0 ? 'bg-resolver-bg-zero' : 'bg-resolver-bg-one']"
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
w-20
|
|
49
|
+
flex flex-shrink-0 justify-center items-center
|
|
50
|
+
:style="{
|
|
51
|
+
backgroundColor: getColor(balloon.problem!.balloonColor!.background_color),
|
|
52
|
+
}"
|
|
53
|
+
>
|
|
54
|
+
<div
|
|
55
|
+
:style="{
|
|
56
|
+
color: getColor(balloon.problem!.balloonColor!.color),
|
|
57
|
+
}"
|
|
58
|
+
>
|
|
59
|
+
{{ balloon.problem.label }}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
flex flex-1 flex-col justify-center items-start
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
class="resolver-team-name"
|
|
68
|
+
truncate overflow-hidden
|
|
69
|
+
>
|
|
70
|
+
{{ showTeamName(balloon.team) }}
|
|
71
|
+
</div>
|
|
72
|
+
<div
|
|
73
|
+
flex flex-row text-sm items-start gap-x-2
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div
|
|
78
|
+
w-32
|
|
79
|
+
flex flex-shrink-0 flex-row justify-start items-center
|
|
80
|
+
>
|
|
81
|
+
{{ balloon.submission.timestampToMinute }}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useElementVisibility } from "@vueuse/core";
|
|
3
|
+
|
|
2
4
|
import type { ContestIndex } from "@xcpcio/core";
|
|
3
5
|
import { getImageSource } from "@xcpcio/core";
|
|
4
6
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import { getLogoFromPreset } from "./logo";
|
|
7
|
+
import { getLogoFromPreset } from "~/composables/logo";
|
|
8
8
|
|
|
9
9
|
const props = defineProps<{
|
|
10
10
|
data: ContestIndex
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { getDataSourceUrl } from "~/composables/query";
|
|
3
|
+
|
|
4
|
+
const dataSourceUrl = getDataSourceUrl();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div
|
|
9
|
+
v-if="dataSourceUrl.length === 0"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
mt-20
|
|
13
|
+
>
|
|
14
|
+
<DataSourceInput />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div
|
|
19
|
+
v-if="dataSourceUrl.length > 0"
|
|
20
|
+
>
|
|
21
|
+
<Balloon
|
|
22
|
+
:data-source-url="dataSourceUrl"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { getDataSourceUrl } from "~/composables/query";
|
|
3
|
+
|
|
4
|
+
const dataSourceUrl = getDataSourceUrl();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<DataSourceInput />
|
|
9
|
+
|
|
10
|
+
<div v-if="dataSourceUrl.length > 0">
|
|
11
|
+
<Board
|
|
12
|
+
:data-source-url="dataSourceUrl"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const dataSourceUrlFromRouteQuery = useRouteQuery(
|
|
5
|
-
"data-source",
|
|
6
|
-
"",
|
|
7
|
-
{ transform: String },
|
|
8
|
-
);
|
|
2
|
+
import { getDataSourceUrl } from "~/composables/query";
|
|
9
3
|
|
|
10
4
|
const { t } = useI18n();
|
|
11
5
|
|
|
12
|
-
const dataSourceUrl =
|
|
6
|
+
const dataSourceUrl = getDataSourceUrl();
|
|
13
7
|
const dataSourceUrlText = "Data Source URL";
|
|
8
|
+
const dataSourceUrlInput = ref(dataSourceUrl.value);
|
|
14
9
|
|
|
10
|
+
const route = useRoute();
|
|
15
11
|
const router = useRouter();
|
|
16
12
|
function go() {
|
|
17
|
-
|
|
18
|
-
router.push(`/board/?data-source=${dataSourceUrl.value.trim()}`);
|
|
19
|
-
}
|
|
13
|
+
router.push(`${route.fullPath}/?data-source=${dataSourceUrlInput.value.trim()}`);
|
|
20
14
|
}
|
|
21
15
|
</script>
|
|
22
16
|
|
|
23
17
|
<template>
|
|
24
18
|
<div
|
|
25
|
-
v-if="
|
|
19
|
+
v-if="dataSourceUrl.length === 0"
|
|
26
20
|
class="flex flex-col items-center"
|
|
27
21
|
>
|
|
28
22
|
<div
|
|
29
23
|
w-128
|
|
30
24
|
>
|
|
31
25
|
<TheInput
|
|
32
|
-
v-model="
|
|
26
|
+
v-model="dataSourceUrlInput"
|
|
33
27
|
w-full
|
|
34
28
|
:placeholder="dataSourceUrlText"
|
|
35
29
|
autocomplete="false"
|
|
@@ -45,17 +39,11 @@ function go() {
|
|
|
45
39
|
<div>
|
|
46
40
|
<button
|
|
47
41
|
m-3 text-sm btn
|
|
48
|
-
:disabled="
|
|
42
|
+
:disabled="dataSourceUrlInput.length === 0"
|
|
49
43
|
@click="go"
|
|
50
44
|
>
|
|
51
45
|
{{ t('button.go') }}
|
|
52
46
|
</button>
|
|
53
47
|
</div>
|
|
54
48
|
</div>
|
|
55
|
-
|
|
56
|
-
<div v-else>
|
|
57
|
-
<Board
|
|
58
|
-
:data-source-url="dataSourceUrlFromRouteQuery"
|
|
59
|
-
/>
|
|
60
|
-
</div>
|
|
61
49
|
</template>
|
|
@@ -7,6 +7,7 @@ import { Rank, RankOptions, createContest, createSubmissions, createTeams, getTi
|
|
|
7
7
|
import type { Contest, Submissions, Teams } from "@xcpcio/core";
|
|
8
8
|
import { ContestState, type Contest as IContest, type Submissions as ISubmissions, type Teams as ITeams } from "@xcpcio/types";
|
|
9
9
|
|
|
10
|
+
import { TITLE_SUFFIX } from "~/composables/constant";
|
|
10
11
|
import type { Item } from "~/components/board/SecondLevelMenu.vue";
|
|
11
12
|
|
|
12
13
|
const props = defineProps<{
|
|
@@ -14,7 +15,7 @@ const props = defineProps<{
|
|
|
14
15
|
}>();
|
|
15
16
|
|
|
16
17
|
const route = useRoute();
|
|
17
|
-
const title = useTitle();
|
|
18
|
+
const title = useTitle(TITLE_SUFFIX);
|
|
18
19
|
const { t } = useI18n();
|
|
19
20
|
|
|
20
21
|
const firstLoaded = ref(false);
|
|
@@ -77,7 +78,7 @@ watch(data, async () => {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
contestData.value = createContest(data.value?.contest as IContest);
|
|
80
|
-
title.value = `${contestData.value.name}
|
|
81
|
+
title.value = `${contestData.value.name} | ${TITLE_SUFFIX}`;
|
|
81
82
|
|
|
82
83
|
teamsData.value = createTeams(data.value?.teams as ITeams);
|
|
83
84
|
submissionsData.value = createSubmissions(data.value?.submissions as ISubmissions);
|
|
@@ -43,7 +43,10 @@ function getStandClassName(t: Team, isRankField = false): string {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const solvedProblemIndex = (rank.value.rankStatistics.getTeamSolvedNumIndex(t.solvedProblemNum) - 1) % 2;
|
|
47
|
+
const rankIndex = (t.rank - 1) % 2;
|
|
48
|
+
|
|
49
|
+
return `stand${solvedProblemIndex}${rankIndex}`;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
function isRenderByVisible() {
|
|
@@ -6,15 +6,21 @@
|
|
|
6
6
|
const { t } = useI18n();
|
|
7
7
|
|
|
8
8
|
const route = useRoute();
|
|
9
|
+
const router = useRouter();
|
|
9
10
|
|
|
10
11
|
const resolverUrl = computed(() => {
|
|
11
12
|
return `https://resolver.xcpcio.com/resolver?xcpcio-data-source=${route.path}`;
|
|
12
13
|
});
|
|
14
|
+
|
|
15
|
+
function goBalloon() {
|
|
16
|
+
router.push(`/balloon/?data-source=${route.path}`);
|
|
17
|
+
}
|
|
13
18
|
</script>
|
|
14
19
|
|
|
15
20
|
<template>
|
|
16
21
|
<div
|
|
17
|
-
|
|
22
|
+
w-full
|
|
23
|
+
flex mt-4 gap-4
|
|
18
24
|
>
|
|
19
25
|
<a
|
|
20
26
|
btn
|
|
@@ -24,5 +30,21 @@ const resolverUrl = computed(() => {
|
|
|
24
30
|
>
|
|
25
31
|
{{ t("type_menu.resolver") }}
|
|
26
32
|
</a>
|
|
33
|
+
|
|
34
|
+
<button
|
|
35
|
+
btn
|
|
36
|
+
title="Balloon"
|
|
37
|
+
@click="goBalloon"
|
|
38
|
+
>
|
|
39
|
+
{{ t('type_menu.balloon') }}
|
|
40
|
+
</button>
|
|
41
|
+
|
|
42
|
+
<button
|
|
43
|
+
btn
|
|
44
|
+
title="Submissions"
|
|
45
|
+
disabled="true"
|
|
46
|
+
>
|
|
47
|
+
{{ t('type_menu.submissions') }}
|
|
48
|
+
</button>
|
|
27
49
|
</div>
|
|
28
50
|
</template>
|
package/src/components.d.ts
CHANGED
|
@@ -8,12 +8,15 @@ export {}
|
|
|
8
8
|
declare module 'vue' {
|
|
9
9
|
export interface GlobalComponents {
|
|
10
10
|
Badge: typeof import('./components/board/Badge.vue')['default']
|
|
11
|
-
Balloon: typeof import('./components/
|
|
11
|
+
Balloon: typeof import('./components/Balloon.vue')['default']
|
|
12
|
+
BalloonBlock: typeof import('./components/BalloonBlock.vue')['default']
|
|
12
13
|
Board: typeof import('./components/board/Board.vue')['default']
|
|
13
14
|
BottomStatistics: typeof import('./components/board/BottomStatistics.vue')['default']
|
|
14
|
-
|
|
15
|
+
ContestIndexUI: typeof import('./components/ContestIndexUI.vue')['default']
|
|
15
16
|
ContestStateBadge: typeof import('./components/board/ContestStateBadge.vue')['default']
|
|
16
|
-
|
|
17
|
+
CustomBalloon: typeof import('./components/CustomBalloon.vue')['default']
|
|
18
|
+
CustomBoard: typeof import('./components/CustomBoard.vue')['default']
|
|
19
|
+
DataSourceInput: typeof import('./components/DataSourceInput.vue')['default']
|
|
17
20
|
Export: typeof import('./components/board/Export.vue')['default']
|
|
18
21
|
Footer: typeof import('./components/Footer.vue')['default']
|
|
19
22
|
GirlIcon: typeof import('./components/icon/GirlIcon.vue')['default']
|
package/src/pages/index.vue
CHANGED
|
@@ -5,9 +5,12 @@ import { useRouteQuery } from "@vueuse/router";
|
|
|
5
5
|
import { createContestIndexList } from "@xcpcio/core";
|
|
6
6
|
import type { ContestIndexList } from "@xcpcio/core";
|
|
7
7
|
|
|
8
|
+
import { TITLE_SUFFIX } from "~/composables/constant";
|
|
8
9
|
import SearchInput from "~/components/SearchInput.vue";
|
|
10
|
+
import ContestIndexUI from "~/components/ContestIndexUI.vue";
|
|
9
11
|
|
|
10
12
|
const { t } = useI18n();
|
|
13
|
+
useTitle(TITLE_SUFFIX);
|
|
11
14
|
|
|
12
15
|
const now = ref(new Date());
|
|
13
16
|
const url = ref(`${window.DATA_HOST}index/contest_list.json?t=${now.value.getTime()}`);
|
|
@@ -106,7 +109,7 @@ watch(searchText, () => {
|
|
|
106
109
|
v-for="item in contestIndexList"
|
|
107
110
|
:key="item.boardLink"
|
|
108
111
|
>
|
|
109
|
-
<
|
|
112
|
+
<ContestIndexUI
|
|
110
113
|
:data="item"
|
|
111
114
|
/>
|
|
112
115
|
</template>
|
package/src/styles/color.css
CHANGED
|
@@ -22,6 +22,14 @@ html {
|
|
|
22
22
|
--theme-status-skipped: #78909C;
|
|
23
23
|
--theme-status-unknown: #000;
|
|
24
24
|
--theme-status-undefined: var(--theme-status-unknown);
|
|
25
|
+
|
|
26
|
+
--theme-resolver-ac: #5eb95e;
|
|
27
|
+
--theme-resolver-wa: #dd514c;
|
|
28
|
+
--theme-resolver-pending: #607D8B;
|
|
29
|
+
--theme-resolver-untouched: #1f1f1f;
|
|
30
|
+
--theme-resolver-selected: #406b82;
|
|
31
|
+
--theme-resolver-bg-0: #3e3e3e;
|
|
32
|
+
--theme-resolver-bg-1: #030712;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
html.dark {
|
package/src/styles/main.css
CHANGED
package/uno.config.ts
CHANGED
|
@@ -25,6 +25,17 @@ export default defineConfig({
|
|
|
25
25
|
900: "#1e3a8a",
|
|
26
26
|
950: "#172554",
|
|
27
27
|
},
|
|
28
|
+
resolver: {
|
|
29
|
+
ac: "var(--theme-resolver-ac)",
|
|
30
|
+
wa: "var(--theme-resolver-wa)",
|
|
31
|
+
pending: "var(--theme-resolver-pending)",
|
|
32
|
+
untouched: "var(--theme-resolver-untouched)",
|
|
33
|
+
selected: "var(--theme-resolver-selected)",
|
|
34
|
+
bg: {
|
|
35
|
+
zero: "var(--theme-resolver-bg-0)",
|
|
36
|
+
one: "var(--theme-resolver-bg-1)",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
28
39
|
},
|
|
29
40
|
},
|
|
30
41
|
shortcuts: [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{g as o,az as l,$ as r,a0 as s,j as u,o as p,c as d,aA as i,k as m}from"./app-dacf61f3.js";const f=o({__name:"TheInput",props:{modelValue:null},emits:["update:modelValue"],setup(_){const{modelValue:e}=l("modelValue");return(a,t)=>r((p(),d("input",i({id:"input","onUpdate:modelValue":t[0]||(t[0]=n=>m(e)?e.value=n:null),type:"text"},a.$attrs,{p:"x-4 y-2","w-full":"",text:"center",bg:"transparent",border:"~ rounded gray-200 dark:gray-700",outline:"none active:none"}),null,16)),[[s,u(e)]])}});export{f as _};
|