@xcpcio/board-app 0.66.2 → 0.68.0
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/404.html +1 -1
- package/dist/assets/Balloon.vue_vue_type_script_setup_true_lang-CVSYxE8q.js +1 -0
- package/dist/assets/{Board-CG69kAf7.css → Board-C89Lc-cb.css} +1 -1
- package/dist/assets/{Board-sX3HmCh3.js → Board-ZYE9iVvT.js} +108 -108
- package/dist/assets/{ContestStateBadge-BqhxSQpe.css → ContestStateBadge-D6uf8wH-.css} +1 -1
- package/dist/assets/ContestStateBadge-M2tyF2R2.js +1 -0
- package/dist/assets/Countdown-fK8Yi5IE.js +1 -0
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-B--dkpEf.js +1 -0
- package/dist/assets/{Footer-BWV6CMUR.js → Footer-5EqvI5Pw.js} +1 -1
- package/dist/assets/{NavBar-CVNz1--5.js → NavBar-Dot9PKUw.js} +1 -1
- package/dist/assets/{Resolver-Cu-OKY1L.js → Resolver-Yd-uWMur.js} +1 -1
- package/dist/assets/{RightArrowIcon-ClzV1Z1J.js → RightArrowIcon-2ctMH32p.js} +1 -1
- package/dist/assets/{TheInput.vue_vue_type_script_setup_true_lang-C21qO8yZ.js → TheInput.vue_vue_type_script_setup_true_lang-BqqBHhl3.js} +1 -1
- package/dist/assets/{Tooltip.vue_vue_type_script_setup_true_lang-D8Y2xuKM.js → Tooltip.vue_vue_type_script_setup_true_lang-BFSx1PX0.js} +1 -1
- package/dist/assets/_...all_-9ILQSzaE.js +1 -0
- package/dist/assets/_...all_-B-QynHK6.js +6 -0
- package/dist/assets/{_name_-CNK60UTb.js → _name_-RaGieVvR.js} +1 -1
- package/dist/assets/{about-Cz2BvibQ.js → about-DNnMqs94.js} +1 -1
- package/dist/assets/board-DmrjMzqp.js +1 -0
- package/dist/assets/{board-layout-DciCGZEs.js → board-layout-BSxiOc_Q.js} +1 -1
- package/dist/assets/{constant-BWnqIcmY.js → constant-D6Nm4zsw.js} +1 -1
- package/dist/assets/{dayjs-Dk3pur30.js → dayjs-txUEIdIq.js} +1 -1
- package/dist/assets/{default-C691w98Q.js → default-DVYI5rLy.js} +1 -1
- package/dist/assets/en-DYzJz2c1.js +1 -0
- package/dist/assets/{headless-CORlMDXV.js → headless-DNk-Av30.js} +1 -1
- package/dist/assets/{home-hTEr1TJm.js → home-DtZxkw_X.js} +1 -1
- package/dist/assets/{index-BiwUz1KO.css → index-BGJAeisZ.css} +1 -1
- package/dist/assets/{index-B4yWQlcU.js → index-BW1DT02C.js} +1 -1
- package/dist/assets/index-BeHHcwg1.js +1 -0
- package/dist/assets/index-BetRF1su.js +1 -0
- package/dist/assets/{index-9_oqoLcR.js → index-DA67idY7.js} +5 -5
- package/dist/assets/{index-D6pv9dGi.js → index-DSyYcmRD.js} +2 -2
- package/dist/assets/index-HzZPY3xn.js +1 -0
- package/dist/assets/{index-layout-CnsXfFdQ.js → index-layout-D1p6y6vz.js} +1 -1
- package/dist/assets/{pagination-DnhQ5r1u.js → pagination-f8dx5hXg.js} +1 -1
- package/dist/assets/{person-U3tcpMKk.js → person-CTZ9LyDi.js} +1 -1
- package/dist/assets/query-DSjYhsbY.js +39 -0
- package/dist/assets/rank-BbnylyV_.js +1 -0
- package/dist/assets/{test-Cflk2EUV.js → test-C0_2hMsN.js} +1 -1
- package/dist/assets/{use-vmodel-Bl_BJeQo.js → use-vmodel-BmoIT9Gg.js} +1 -1
- package/dist/assets/useQueryBoardData-BfELgSoK.js +1 -0
- package/dist/assets/{user-LgM0y5TK.js → user-Dx6zM0gp.js} +1 -1
- package/dist/assets/{virtual_pwa-register-72WHXVVX.js → virtual_pwa-register-BfEpgXzG.js} +1 -1
- package/dist/assets/zh-CN-Cbd_t3aN.js +1 -0
- package/dist/index.html +1 -1
- package/dist/sw.js +1 -1
- package/package.json +3 -3
- package/src/auto-imports.d.ts +19 -4
- package/src/components/CustomBalloon.vue +1 -3
- package/src/components/CustomBoard.vue +1 -3
- package/src/components/CustomCountdown.vue +1 -3
- package/src/components/CustomResolver.vue +1 -3
- package/src/components/DataSourceInput.vue +1 -3
- package/src/components/board/Badge.vue +1 -1
- package/src/components/board/Board.vue +5 -20
- package/src/components/board/OptionsModal.vue +1 -1
- package/src/components/board/Progress.vue +1 -2
- package/src/components/board/SecondLevelMenu.vue +1 -2
- package/src/components/board/Standings.vue +1 -1
- package/src/components/board/TeamInfo.vue +116 -0
- package/src/components/board/TeamInfoModal.vue +16 -13
- package/src/components/board/TeamUI.vue +2 -1
- package/src/components/board/Utility.vue +1 -2
- package/src/components.d.ts +1 -0
- package/src/composables/query.ts +46 -6
- package/src/composables/rating.ts +6 -2
- package/src/composables/useRouteQueryWithoutParam.ts +157 -0
- package/src/pages/[...all].vue +1 -7
- package/src/pages/index.vue +1 -2
- package/dist/assets/Balloon.vue_vue_type_script_setup_true_lang-Cd-QNnDt.js +0 -1
- package/dist/assets/ContestStateBadge-Cad99MQ4.js +0 -1
- package/dist/assets/Countdown-Dhia-u0Y.js +0 -1
- package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-DVWnGqx-.js +0 -1
- package/dist/assets/_...all_-D0bkV7XM.js +0 -1
- package/dist/assets/_...all_-DJwQ4yj4.js +0 -6
- package/dist/assets/board-CAZpPFLb.js +0 -1
- package/dist/assets/contest-CgNGJetR.js +0 -39
- package/dist/assets/en-78xzu5B_.js +0 -1
- package/dist/assets/index-BX3T_nMP.js +0 -1
- package/dist/assets/index-DncwAlQA.js +0 -1
- package/dist/assets/index-mCQ-iQmf.js +0 -1
- package/dist/assets/query-CDfDbkZE.js +0 -1
- package/dist/assets/rank-B4f6mi_k.js +0 -1
- package/dist/assets/zh-CN-32UqCzRy.js +0 -1
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.68.0",
|
|
5
5
|
"description": "The ICPC Series Competition Leaderboard Visualization Engine",
|
|
6
6
|
"author": "Dup4 <hi@dup4.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"vue-router": "^4.5.1",
|
|
54
54
|
"vue-search-select": "^3.2.0",
|
|
55
55
|
"vue-toast-notification": "^3.1.3",
|
|
56
|
-
"@xcpcio/core": "0.
|
|
57
|
-
"@xcpcio/types": "0.
|
|
56
|
+
"@xcpcio/core": "0.68.0",
|
|
57
|
+
"@xcpcio/types": "0.68.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@iconify/json": "^2.2.394",
|
package/src/auto-imports.d.ts
CHANGED
|
@@ -53,7 +53,6 @@ declare global {
|
|
|
53
53
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
|
54
54
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
|
55
55
|
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
|
|
56
|
-
const getDataSourceUrl: typeof import('./composables/query')['getDataSourceUrl']
|
|
57
56
|
const getLocalStorageKeyForFilterOrganizations: typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterOrganizations']
|
|
58
57
|
const getLocalStorageKeyForFilterTeams: typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterTeams']
|
|
59
58
|
const getMedalColor: typeof import('./composables/color')['getMedalColor']
|
|
@@ -260,11 +259,18 @@ declare global {
|
|
|
260
259
|
const usePreferredReducedTransparency: typeof import('@vueuse/core')['usePreferredReducedTransparency']
|
|
261
260
|
const usePrevious: typeof import('@vueuse/core')['usePrevious']
|
|
262
261
|
const useQueryBoardData: typeof import('./composables/useQueryBoardData')['useQueryBoardData']
|
|
262
|
+
const useQueryForBattleOfGiants: typeof import('./composables/query')['useQueryForBattleOfGiants']
|
|
263
|
+
const useQueryForComponent: typeof import('./composables/query')['useQueryForComponent']
|
|
264
|
+
const useQueryForDataSourceUrl: typeof import('./composables/query')['useQueryForDataSourceUrl']
|
|
265
|
+
const useQueryForGroup: typeof import('./composables/query')['useQueryForGroup']
|
|
266
|
+
const useQueryForProgressRatio: typeof import('./composables/query')['useQueryForProgressRatio']
|
|
267
|
+
const useQueryForReplayStartTime: typeof import('./composables/query')['useQueryForReplayStartTime']
|
|
268
|
+
const useQueryForSearch: typeof import('./composables/query')['useQueryForSearch']
|
|
263
269
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
|
264
270
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
|
265
271
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
|
266
272
|
const useRoute: typeof import('vue-router')['useRoute']
|
|
267
|
-
const
|
|
273
|
+
const useRouteQueryWithoutParam: typeof import('./composables/useRouteQueryWithoutParam')['useRouteQueryWithoutParam']
|
|
268
274
|
const useRouter: typeof import('vue-router')['useRouter']
|
|
269
275
|
const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth']
|
|
270
276
|
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
|
@@ -353,6 +359,9 @@ declare global {
|
|
|
353
359
|
// @ts-ignore
|
|
354
360
|
export type { BoardData } from './composables/useQueryBoardData'
|
|
355
361
|
import('./composables/useQueryBoardData')
|
|
362
|
+
// @ts-ignore
|
|
363
|
+
export type { RouteQueryValueRaw, RouteHashValueRaw, ReactiveRouteOptions, ReactiveRouteOptionsWithTransform } from './composables/useRouteQueryWithoutParam'
|
|
364
|
+
import('./composables/useRouteQueryWithoutParam')
|
|
356
365
|
}
|
|
357
366
|
|
|
358
367
|
// for vue template auto import
|
|
@@ -407,7 +416,6 @@ declare module 'vue' {
|
|
|
407
416
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
|
408
417
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
|
409
418
|
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
|
|
410
|
-
readonly getDataSourceUrl: UnwrapRef<typeof import('./composables/query')['getDataSourceUrl']>
|
|
411
419
|
readonly getLocalStorageKeyForFilterOrganizations: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterOrganizations']>
|
|
412
420
|
readonly getLocalStorageKeyForFilterTeams: UnwrapRef<typeof import('./composables/useLocalStorage')['getLocalStorageKeyForFilterTeams']>
|
|
413
421
|
readonly getMedalColor: UnwrapRef<typeof import('./composables/color')['getMedalColor']>
|
|
@@ -614,11 +622,18 @@ declare module 'vue' {
|
|
|
614
622
|
readonly usePreferredReducedTransparency: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedTransparency']>
|
|
615
623
|
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
|
|
616
624
|
readonly useQueryBoardData: UnwrapRef<typeof import('./composables/useQueryBoardData')['useQueryBoardData']>
|
|
625
|
+
readonly useQueryForBattleOfGiants: UnwrapRef<typeof import('./composables/query')['useQueryForBattleOfGiants']>
|
|
626
|
+
readonly useQueryForComponent: UnwrapRef<typeof import('./composables/query')['useQueryForComponent']>
|
|
627
|
+
readonly useQueryForDataSourceUrl: UnwrapRef<typeof import('./composables/query')['useQueryForDataSourceUrl']>
|
|
628
|
+
readonly useQueryForGroup: UnwrapRef<typeof import('./composables/query')['useQueryForGroup']>
|
|
629
|
+
readonly useQueryForProgressRatio: UnwrapRef<typeof import('./composables/query')['useQueryForProgressRatio']>
|
|
630
|
+
readonly useQueryForReplayStartTime: UnwrapRef<typeof import('./composables/query')['useQueryForReplayStartTime']>
|
|
631
|
+
readonly useQueryForSearch: UnwrapRef<typeof import('./composables/query')['useQueryForSearch']>
|
|
617
632
|
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
|
|
618
633
|
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
|
|
619
634
|
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
|
|
620
635
|
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
|
621
|
-
readonly
|
|
636
|
+
readonly useRouteQueryWithoutParam: UnwrapRef<typeof import('./composables/useRouteQueryWithoutParam')['useRouteQueryWithoutParam']>
|
|
622
637
|
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
|
623
638
|
readonly useSSRWidth: UnwrapRef<typeof import('@vueuse/core')['useSSRWidth']>
|
|
624
639
|
readonly useScreenOrientation: UnwrapRef<typeof import('@vueuse/core')['useScreenOrientation']>
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { getDataSourceUrl } from "@board/composables/query";
|
|
3
|
-
|
|
4
2
|
const { t } = useI18n();
|
|
5
3
|
|
|
6
|
-
const dataSourceUrl =
|
|
4
|
+
const dataSourceUrl = useQueryForDataSourceUrl();
|
|
7
5
|
const dataSourceUrlText = "Data Source URL";
|
|
8
6
|
const dataSourceUrlInput = ref(dataSourceUrl.value);
|
|
9
7
|
|
|
@@ -3,12 +3,6 @@ import type { Item } from "@board/components/board/SecondLevelMenu.vue";
|
|
|
3
3
|
import type { Contest, Submissions, Teams } from "@xcpcio/core";
|
|
4
4
|
import type { Contest as IContest, Submissions as ISubmissions, Teams as ITeams, Lang } from "@xcpcio/types";
|
|
5
5
|
|
|
6
|
-
import FilterModal from "@board/components/board/FilterModal.vue";
|
|
7
|
-
|
|
8
|
-
import { TITLE_SUFFIX } from "@board/composables/constant";
|
|
9
|
-
import { onKeyStroke, useDocumentVisibility, useIntervalFn, useNow } from "@vueuse/core";
|
|
10
|
-
|
|
11
|
-
import { useRouteQuery } from "@vueuse/router";
|
|
12
6
|
import { createContest, createSubmissions, createTeams, getImageSource, getTimeDiff, Rank, RankOptions } from "@xcpcio/core";
|
|
13
7
|
import { ContestState } from "@xcpcio/types";
|
|
14
8
|
|
|
@@ -34,10 +28,6 @@ const contestName = ref("");
|
|
|
34
28
|
|
|
35
29
|
const enableAutoScroll = ref(false);
|
|
36
30
|
|
|
37
|
-
function fixPath(path: string) {
|
|
38
|
-
return path.replaceAll("%2F", "/");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
31
|
(() => {
|
|
42
32
|
const filterOrganizations = useLocalStorageForFilterOrganizations();
|
|
43
33
|
const filterTeams = useLocalStorageForFilterTeams();
|
|
@@ -52,7 +42,7 @@ function fixPath(path: string) {
|
|
|
52
42
|
})();
|
|
53
43
|
|
|
54
44
|
(() => {
|
|
55
|
-
const routeQueryForBattleOfGiants =
|
|
45
|
+
const routeQueryForBattleOfGiants = useQueryForBattleOfGiants();
|
|
56
46
|
if (
|
|
57
47
|
routeQueryForBattleOfGiants.value !== null
|
|
58
48
|
&& routeQueryForBattleOfGiants.value !== undefined
|
|
@@ -70,17 +60,12 @@ function onChangeCurrentGroup(nextGroup: string) {
|
|
|
70
60
|
rankOptions.value.setGroup(nextGroup);
|
|
71
61
|
}
|
|
72
62
|
(() => {
|
|
73
|
-
const currentGroupFromRouteQuery =
|
|
74
|
-
/* name */ "group",
|
|
75
|
-
/* defaultValue */ "all",
|
|
76
|
-
{ transform: String },
|
|
77
|
-
);
|
|
78
|
-
|
|
63
|
+
const currentGroupFromRouteQuery = useQueryForGroup();
|
|
79
64
|
currentGroup.value = currentGroupFromRouteQuery.value;
|
|
80
65
|
rankOptions.value.setGroup(currentGroupFromRouteQuery.value);
|
|
81
66
|
})();
|
|
82
67
|
|
|
83
|
-
const replayStartTime =
|
|
68
|
+
const replayStartTime = useQueryForReplayStartTime();
|
|
84
69
|
|
|
85
70
|
const isReBuildRank = ref(false);
|
|
86
71
|
function reBuildRank(options = { force: false }) {
|
|
@@ -109,7 +94,7 @@ function updateContestName() {
|
|
|
109
94
|
title.value = `${contestName.value} | ${TITLE_SUFFIX}`;
|
|
110
95
|
}
|
|
111
96
|
|
|
112
|
-
const { data, isError, error, refetch } = useQueryBoardData(props.dataSourceUrl ??
|
|
97
|
+
const { data, isError, error, refetch } = useQueryBoardData(props.dataSourceUrl ?? route.path, now);
|
|
113
98
|
watch(data, async () => {
|
|
114
99
|
if (data.value === null || data.value === undefined) {
|
|
115
100
|
return;
|
|
@@ -420,7 +405,7 @@ const widthClass = "sm:w-[1260px] xl:w-screen";
|
|
|
420
405
|
>
|
|
421
406
|
<div class="max-w-[92%]">
|
|
422
407
|
<img
|
|
423
|
-
:src="getImageSource(rank.contest.banner, `${DATA_HOST}${
|
|
408
|
+
:src="getImageSource(rank.contest.banner, `${DATA_HOST}${route.path.slice(1)}`)"
|
|
424
409
|
class="w-screen"
|
|
425
410
|
alt="banner"
|
|
426
411
|
>
|
|
@@ -66,7 +66,7 @@ const teamsOptions = computed(() => {
|
|
|
66
66
|
return res;
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
const routeQueryForBattleOfGiants =
|
|
69
|
+
const routeQueryForBattleOfGiants = useQueryForBattleOfGiants();
|
|
70
70
|
function persistBattleOfGiants() {
|
|
71
71
|
if (rankOptions.value.battleOfGiants.persist) {
|
|
72
72
|
routeQueryForBattleOfGiants.value = rankOptions.value.battleOfGiants.ToBase64();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Rank, RankOptions } from "@xcpcio/core";
|
|
3
|
-
import { useRouteQuery } from "@vueuse/router";
|
|
4
3
|
import { createDayJS, getTimeDiff } from "@xcpcio/core";
|
|
5
4
|
import { ContestState } from "@xcpcio/types";
|
|
6
5
|
|
|
@@ -44,7 +43,7 @@ const isDragging = ref(false);
|
|
|
44
43
|
const dragWidth = ref(0);
|
|
45
44
|
const barWidth = ref(props.width);
|
|
46
45
|
const barWidthPX = ref(0);
|
|
47
|
-
const progressRatio =
|
|
46
|
+
const progressRatio = useQueryForProgressRatio();
|
|
48
47
|
|
|
49
48
|
const scroll = ref<HTMLElement>(null as unknown as HTMLElement);
|
|
50
49
|
const mask = ref<HTMLElement>(null as unknown as HTMLElement);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { I18nText } from "@xcpcio/core";
|
|
3
3
|
import type { Lang } from "@xcpcio/types";
|
|
4
|
-
import { useRouteQuery } from "@vueuse/router";
|
|
5
4
|
|
|
6
5
|
export interface Item {
|
|
7
6
|
title: string | I18nText;
|
|
@@ -33,7 +32,7 @@ const defaultType = computed(() => {
|
|
|
33
32
|
return props.items?.[0].keyword;
|
|
34
33
|
});
|
|
35
34
|
|
|
36
|
-
const currentItemFromRouteQuery =
|
|
35
|
+
const currentItemFromRouteQuery = useRouteQueryWithoutParam(
|
|
37
36
|
props.queryParamName,
|
|
38
37
|
defaultType.value,
|
|
39
38
|
{ transform: String },
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Rank, Team } from "@xcpcio/core";
|
|
3
|
+
import type { Lang } from "@xcpcio/types";
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
rank: Rank;
|
|
7
|
+
team: Team;
|
|
8
|
+
}>();
|
|
9
|
+
|
|
10
|
+
const { t, locale } = useI18n();
|
|
11
|
+
const lang = computed(() => locale.value as unknown as Lang);
|
|
12
|
+
|
|
13
|
+
const rank = computed(() => props.rank);
|
|
14
|
+
const team = computed(() => props.team);
|
|
15
|
+
|
|
16
|
+
const photoLoadError = ref(false);
|
|
17
|
+
function handlePhotoError() {
|
|
18
|
+
photoLoadError.value = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hasPhoto = computed(() => {
|
|
22
|
+
if (team.value.missingPhoto || photoLoadError.value) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return rank.value.contest.options.teamPhotoTemplate || team.value.photo;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const teamPhoto = computed(() => {
|
|
30
|
+
if (team.value.photo?.url) {
|
|
31
|
+
return {
|
|
32
|
+
url: team.value.photo.url,
|
|
33
|
+
width: team.value.photo.width,
|
|
34
|
+
height: team.value.photo.height,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const template = rank.value.contest.options.teamPhotoTemplate;
|
|
39
|
+
if (template?.url) {
|
|
40
|
+
return {
|
|
41
|
+
url: template.url.replace(/\$\{team_id\}/, team.value.id),
|
|
42
|
+
width: template.width,
|
|
43
|
+
height: template.height,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { url: "", width: undefined, height: undefined };
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const groupNames = computed(() => {
|
|
51
|
+
return team.value.group
|
|
52
|
+
.map((groupId) => {
|
|
53
|
+
const group = rank.value.contest.group.get(groupId);
|
|
54
|
+
return group ? group.name.getOrDefault(lang.value) : groupId;
|
|
55
|
+
})
|
|
56
|
+
.join(", ");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const tableRows = computed(() => {
|
|
60
|
+
const rows = [
|
|
61
|
+
{ label: t("team_info.rank"), value: team.value.rank, show: true },
|
|
62
|
+
{ label: t("team_info.team_name"), value: team.value.name.getOrDefault(lang.value), show: true },
|
|
63
|
+
{ label: t("team_info.organization"), value: team.value.organization, show: !!team.value.organization },
|
|
64
|
+
{ label: t("team_info.group"), value: groupNames.value, show: team.value.group.length > 0 },
|
|
65
|
+
{ label: t("team_info.members"), value: team.value.membersToString(lang.value), show: team.value.members.length > 0 },
|
|
66
|
+
{ label: t("team_info.coaches"), value: team.value.coachesToString(lang.value), show: team.value.coaches.length > 0 },
|
|
67
|
+
{ label: t("team_info.location"), value: team.value.location, show: !!team.value.location },
|
|
68
|
+
];
|
|
69
|
+
return rows.filter(row => row.show);
|
|
70
|
+
});
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<template>
|
|
74
|
+
<div
|
|
75
|
+
flex w-full
|
|
76
|
+
:class="hasPhoto ? 'flex-row gap-8' : 'justify-center'"
|
|
77
|
+
>
|
|
78
|
+
<div
|
|
79
|
+
:class="hasPhoto ? 'flex-1' : 'w-1/2'"
|
|
80
|
+
flex items-center justify-center
|
|
81
|
+
>
|
|
82
|
+
<table w-full text-lg text-left text-gray-900 dark:text-gray-100>
|
|
83
|
+
<tbody>
|
|
84
|
+
<tr
|
|
85
|
+
v-for="(row, index) in tableRows"
|
|
86
|
+
:key="row.label"
|
|
87
|
+
:class="index % 2 === 0 ? 'bg-gray-100 dark:bg-gray-700' : 'bg-white dark:bg-gray-800'"
|
|
88
|
+
>
|
|
89
|
+
<td px-4 py-2>
|
|
90
|
+
{{ row.label }}
|
|
91
|
+
</td>
|
|
92
|
+
<td px-4 py-2>
|
|
93
|
+
{{ row.value }}
|
|
94
|
+
</td>
|
|
95
|
+
</tr>
|
|
96
|
+
</tbody>
|
|
97
|
+
</table>
|
|
98
|
+
</div>
|
|
99
|
+
<div v-if="hasPhoto" flex-1>
|
|
100
|
+
<div flex items-center justify-center>
|
|
101
|
+
<a
|
|
102
|
+
class="max-w-[98%]"
|
|
103
|
+
target="_blank" rel="nofollow" :href="teamPhoto.url"
|
|
104
|
+
>
|
|
105
|
+
<img
|
|
106
|
+
:src="teamPhoto.url"
|
|
107
|
+
:width="teamPhoto.width"
|
|
108
|
+
:height="teamPhoto.height"
|
|
109
|
+
alt="team photo"
|
|
110
|
+
@error="handlePhotoError"
|
|
111
|
+
>
|
|
112
|
+
</a>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
@@ -25,7 +25,7 @@ const isHidden = computed({
|
|
|
25
25
|
},
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const currentType = ref("
|
|
28
|
+
const currentType = ref("info");
|
|
29
29
|
|
|
30
30
|
const rank = computed(() => props.rank);
|
|
31
31
|
const team = computed(() => props.team);
|
|
@@ -39,21 +39,14 @@ const headerTitle = computed(() => {
|
|
|
39
39
|
|
|
40
40
|
res += `${team.value.name.getOrDefault(lang.value)}`;
|
|
41
41
|
|
|
42
|
-
if (team.value.members && team.value.members.length > 0) {
|
|
43
|
-
res += ` - ${team.value.membersToString(lang.value)}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (team.value.coaches && team.value.coaches.length > 0) {
|
|
47
|
-
res += ` - ${team.value.coachesToString(lang.value)}(coach)`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
42
|
return res;
|
|
51
43
|
});
|
|
52
44
|
|
|
45
|
+
const TYPE_INFO = "info";
|
|
53
46
|
const TYPE_SUBMISSIONS = "submissions";
|
|
54
47
|
const TYPE_STATISTICS = "statistics";
|
|
55
48
|
const TYPE_AWARDS = "awards";
|
|
56
|
-
const types = [TYPE_SUBMISSIONS, TYPE_STATISTICS, TYPE_AWARDS];
|
|
49
|
+
const types = [TYPE_INFO, TYPE_SUBMISSIONS, TYPE_STATISTICS, TYPE_AWARDS];
|
|
57
50
|
</script>
|
|
58
51
|
|
|
59
52
|
<template>
|
|
@@ -75,13 +68,13 @@ const types = [TYPE_SUBMISSIONS, TYPE_STATISTICS, TYPE_AWARDS];
|
|
|
75
68
|
space-y-3 md:space-y-0
|
|
76
69
|
>
|
|
77
70
|
<div
|
|
78
|
-
flex flex-row
|
|
79
|
-
space-x-
|
|
71
|
+
flex flex-row items-center justify-center
|
|
72
|
+
space-x-4
|
|
80
73
|
>
|
|
81
74
|
<Badge
|
|
82
75
|
v-if="team.badge"
|
|
83
76
|
:image="team.badge"
|
|
84
|
-
width-class="h-
|
|
77
|
+
width-class="h-16 w-16"
|
|
85
78
|
/>
|
|
86
79
|
|
|
87
80
|
<Tooltip>
|
|
@@ -120,6 +113,16 @@ const types = [TYPE_SUBMISSIONS, TYPE_STATISTICS, TYPE_AWARDS];
|
|
|
120
113
|
font-bold font-mono
|
|
121
114
|
flex items-center justify-center
|
|
122
115
|
>
|
|
116
|
+
<div
|
|
117
|
+
v-if="currentType === TYPE_INFO"
|
|
118
|
+
w-full
|
|
119
|
+
>
|
|
120
|
+
<TeamInfo
|
|
121
|
+
:rank="rank"
|
|
122
|
+
:team="team"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
123
126
|
<div
|
|
124
127
|
v-if="currentType === TYPE_SUBMISSIONS"
|
|
125
128
|
w-full
|
|
@@ -91,10 +91,11 @@ function isRenderByVisible() {
|
|
|
91
91
|
<td
|
|
92
92
|
v-if="rank.contest.badge && team.badge && isRenderByVisible()"
|
|
93
93
|
class="empty flex items-center justify-center"
|
|
94
|
+
style="padding: 0px !important; margin: 0px !important;"
|
|
94
95
|
>
|
|
95
96
|
<Badge
|
|
96
97
|
:image="team.badge"
|
|
97
|
-
width-class="
|
|
98
|
+
width-class="w-full h-full"
|
|
98
99
|
/>
|
|
99
100
|
</td>
|
|
100
101
|
<td
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Rank } from "@xcpcio/core";
|
|
3
|
-
import { useRouteQuery } from "@vueuse/router";
|
|
4
3
|
|
|
5
4
|
const props = defineProps<{
|
|
6
5
|
rank: Rank;
|
|
@@ -10,7 +9,7 @@ const { t } = useI18n();
|
|
|
10
9
|
|
|
11
10
|
const route = useRoute();
|
|
12
11
|
const router = useRouter();
|
|
13
|
-
const component =
|
|
12
|
+
const component = useQueryForComponent();
|
|
14
13
|
|
|
15
14
|
function goResolver() {
|
|
16
15
|
if (window.DATA_SOURCE) {
|
package/src/components.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ declare module 'vue' {
|
|
|
60
60
|
SubmitHeatMap: typeof import('./components/board/SubmitHeatMap.vue')['default']
|
|
61
61
|
TablePagination: typeof import('./components/table/TablePagination.vue')['default']
|
|
62
62
|
TeamAwards: typeof import('./components/board/TeamAwards.vue')['default']
|
|
63
|
+
TeamInfo: typeof import('./components/board/TeamInfo.vue')['default']
|
|
63
64
|
TeamInfoModal: typeof import('./components/board/TeamInfoModal.vue')['default']
|
|
64
65
|
TeamProblemBlock: typeof import('./components/board/TeamProblemBlock.vue')['default']
|
|
65
66
|
TeamUI: typeof import('./components/board/TeamUI.vue')['default']
|
package/src/composables/query.ts
CHANGED
|
@@ -1,17 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRouteQueryWithoutParam } from "./useRouteQueryWithoutParam";
|
|
2
2
|
|
|
3
|
-
export function
|
|
4
|
-
return
|
|
5
|
-
"
|
|
3
|
+
export function useQueryForSearch() {
|
|
4
|
+
return useRouteQueryWithoutParam(
|
|
5
|
+
"s",
|
|
6
6
|
"",
|
|
7
7
|
{ transform: String },
|
|
8
8
|
);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function
|
|
12
|
-
return
|
|
11
|
+
export function useQueryForDataSourceUrl() {
|
|
12
|
+
return useRouteQueryWithoutParam(
|
|
13
|
+
"data-source-url",
|
|
14
|
+
"",
|
|
15
|
+
{ transform: String },
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useQueryForGroup() {
|
|
20
|
+
return useRouteQueryWithoutParam(
|
|
21
|
+
"group",
|
|
22
|
+
"all",
|
|
23
|
+
{ transform: String },
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useQueryForReplayStartTime() {
|
|
28
|
+
return useRouteQueryWithoutParam(
|
|
29
|
+
"replay-start-time",
|
|
30
|
+
"0",
|
|
31
|
+
{ transform: Number },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useQueryForProgressRatio() {
|
|
36
|
+
return useRouteQueryWithoutParam(
|
|
37
|
+
"progress-ratio",
|
|
38
|
+
-1,
|
|
39
|
+
{ transform: Number },
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useQueryForBattleOfGiants() {
|
|
44
|
+
return useRouteQueryWithoutParam(
|
|
13
45
|
"battle-of-giants",
|
|
14
46
|
"",
|
|
15
47
|
{ transform: String },
|
|
16
48
|
);
|
|
17
49
|
}
|
|
50
|
+
|
|
51
|
+
export function useQueryForComponent() {
|
|
52
|
+
return useRouteQueryWithoutParam(
|
|
53
|
+
"component",
|
|
54
|
+
"board",
|
|
55
|
+
{ transform: String },
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RatingUser } from "@xcpcio/core";
|
|
2
|
+
import type { Lang } from "@xcpcio/types";
|
|
2
3
|
import { RatingLevelToString, RatingUtility } from "@xcpcio/core";
|
|
3
4
|
|
|
4
5
|
interface RatingGraphData {
|
|
@@ -27,6 +28,9 @@ export function getRatingGraphOptions(
|
|
|
27
28
|
) {
|
|
28
29
|
const data: RatingGraphData[] = [];
|
|
29
30
|
|
|
31
|
+
const { locale } = useI18n();
|
|
32
|
+
const lang = locale.value as unknown as Lang;
|
|
33
|
+
|
|
30
34
|
{
|
|
31
35
|
let oldRating = 0;
|
|
32
36
|
for (const h of ratingUser.ratingHistories) {
|
|
@@ -35,10 +39,10 @@ export function getRatingGraphOptions(
|
|
|
35
39
|
d.y = h.rating;
|
|
36
40
|
d.diffRating = getDiffRating(oldRating, h.rating);
|
|
37
41
|
d.rank = h.rank;
|
|
38
|
-
d.contestName = h.contestName;
|
|
42
|
+
d.contestName = h.contestName.getOrDefault(lang);
|
|
39
43
|
d.link = `/board/${h.contestID}`;
|
|
40
44
|
d.contestTime = h.contestTime.format("YYYY-MM-DD HH:mm:ss");
|
|
41
|
-
d.teamName = h.teamName;
|
|
45
|
+
d.teamName = h.teamName.getOrDefault(lang);
|
|
42
46
|
d.ratingTitle = RatingLevelToString[RatingUtility.getRatingLevel(h.rating)];
|
|
43
47
|
data.push(d);
|
|
44
48
|
|