├── .editorconfig ├── .eslintignore ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── nkuoj-favicon.ai ├── nkuoj-slogan.ai ├── readme-resources │ ├── admin.png │ ├── home.png │ ├── login.png │ └── submission.png └── readme.md ├── index.html ├── package-lock.json ├── package.json ├── public ├── favicon-tju.ico ├── favicon.ico ├── main.tju.css ├── nkuoj-slogan-light.svg ├── nkuoj-slogan.svg ├── pattern-light.svg ├── pattern.svg ├── qq-group.jpg ├── tjuoj-slogan-light.svg └── tjuoj-slogan.svg ├── src ├── App.vue ├── assets │ └── main.css ├── components │ ├── admin │ │ ├── CardManageProblem.vue │ │ ├── CardRanking.vue │ │ ├── CardRejudge.vue │ │ └── CardViewProblemSubmissions.vue │ ├── headbar │ │ ├── HeadBarAdmin.vue │ │ ├── HeadBarPublic.vue │ │ ├── HeadBarStrict.vue │ │ └── HeadBarUniversal.vue │ ├── icons │ │ ├── IconArrowClockwise.vue │ │ ├── IconArrowClockwiseSmall.vue │ │ ├── IconBoxArrowInUpRightSmall.vue │ │ ├── IconCheck2Circle.vue │ │ ├── IconCheckAll.vue │ │ ├── IconChevronDoubleRightSmall.vue │ │ ├── IconClipboardCheck.vue │ │ ├── IconClock.vue │ │ ├── IconClockHistory.vue │ │ ├── IconCodeSquare.vue │ │ ├── IconDownloadSmall.vue │ │ ├── IconEasel.vue │ │ ├── IconFileXLarge.vue │ │ ├── IconFunnelFillSmall.vue │ │ ├── IconFunnelSmall.vue │ │ ├── IconHouse.vue │ │ ├── IconJournalCode.vue │ │ ├── IconJournalPlus.vue │ │ ├── IconJump.vue │ │ ├── IconLightBulbSmall.vue │ │ ├── IconListColumnsReverse.vue │ │ ├── IconListOL.vue │ │ ├── IconMegaphone.vue │ │ ├── IconPDFLarge.vue │ │ ├── IconPencilSquare.vue │ │ ├── IconPerson.vue │ │ ├── IconReception.vue │ │ ├── IconReplySmall.vue │ │ ├── IconSend.vue │ │ ├── IconSendCheck.vue │ │ ├── IconSliders.vue │ │ ├── IconTrophy.vue │ │ ├── IconUIChecks.vue │ │ └── IconUIChecksGrid.vue │ ├── modal │ │ ├── ModalAddContestExam.vue │ │ ├── ModalAddCourse.vue │ │ ├── ModalAddCoursePasscode.vue │ │ ├── ModalAdminIndexProblem.vue │ │ ├── ModalAdminIndexProblemSet.vue │ │ ├── ModalAdminProblemNew.vue │ │ ├── ModalAdminSelectProblem.vue │ │ ├── ModalBase.vue │ │ ├── ModalCompilationOutput.vue │ │ ├── ModalConfirmBox.vue │ │ ├── ModalFilter.vue │ │ ├── ModalJumpToSubmission.vue │ │ ├── ModalLogin.vue │ │ ├── ModalMsgBox.vue │ │ ├── ModalMySubmissions.vue │ │ ├── ModalPreviewMarkdown.vue │ │ ├── ModalRejudgeProgressing.vue │ │ ├── ModalResetPwd.vue │ │ ├── ModalSignup.vue │ │ ├── ModalSubmissionStatusTracing.vue │ │ ├── ModalSubmitProblem.vue │ │ └── ModalUpdateProfile.vue │ ├── panels │ │ ├── PanelAssignment.vue │ │ ├── PanelBulletin.vue │ │ ├── PanelContestExam.vue │ │ ├── PanelCourse.vue │ │ ├── PanelJump.vue │ │ ├── PanelStrictContest.vue │ │ └── PanelStrictExam.vue │ ├── problem │ │ ├── ButtonSubmit.vue │ │ ├── CardProblemContent.vue │ │ └── DropdownNavigator.vue │ ├── ranking │ │ ├── RankingTableContest.vue │ │ └── RankingTableExam.vue │ ├── submission │ │ ├── CardCode.vue │ │ └── CardTestCase.vue │ └── wrapper │ │ ├── CardProgressBar.vue │ │ └── Pagination.vue ├── config.js ├── main.js ├── pages │ ├── AdminHomePage.vue │ ├── AdminProblemContentPage.vue │ ├── AdminProblemDataPage.vue │ ├── AdminProblemPage.vue │ ├── AdminProblemSpecialJudgePage.vue │ ├── AdminProblemSubmissionsPage.vue │ ├── AdminRejudgePage.vue │ ├── AlgoAssociationPage.vue │ ├── AssignmentPage.vue │ ├── BulletinContentPage.vue │ ├── BulletinPage.vue │ ├── ContestPage.vue │ ├── ContestRankingPage.vue │ ├── CoursePage.vue │ ├── ExamPage.vue │ ├── ExamRankingPage.vue │ ├── HomePage.vue │ ├── LoginPage.vue │ ├── LogoutPage.vue │ ├── NotFoundPage.vue │ ├── ProblemListPage.vue │ ├── ProblemPage.vue │ ├── ProfilePage.vue │ ├── SiteSubmissionPage.vue │ ├── StrictHomePage.vue │ └── SubmissionPage.vue ├── router │ └── index.js ├── stores │ ├── filter-data.js │ ├── strict-mode.js │ └── user-data.js ├── templates │ ├── AppAdmin.vue │ ├── AppPublic.vue │ ├── AppStrict.vue │ └── AppUniversal.vue └── util │ ├── compile-markdown.js │ ├── date-to-str.js │ ├── encrypt.js │ ├── http-code-to-str.js │ ├── lang-code-to-text.js │ ├── ordinal-number-to-str.js │ ├── status-code-to-str-short.js │ ├── status-code-to-str.js │ ├── status-code-to-variant-str.js │ └── uid-to-str.js └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | /node_modules/ 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .github 3 | node_modules 4 | /dist 5 | 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Front End of NKU-OJ 2 | 3 | ![Screenshot of the Login Page](assets/readme-resources/login.png?raw=true "Login Page") 4 | ![Screenshot of the Home Page](assets/readme-resources/home.png?raw=true "Home Page") 5 | ![Screenshot of the Submission Page](assets/readme-resources/submission.png?raw=true "Submission Page") 6 | ![Screenshot of the Admin Page](assets/readme-resources/admin.png?raw=true "Admin Page") 7 | 8 | NKUOJ Front End is a website designed for the Online Judge System of Nankai University (NKU-OJ) with vue.js v3 and Vite. 9 | 10 | ## Features 11 | 12 | + Access control with all pages requiring user logged in; 13 | + An Embedded administrator dashboard (Under development); 14 | + Allowing users to list and view public problems; 15 | + Supporting users to subscribe courses, assignments, exams and contests; 16 | + Pages to view problems, courses, assignments, exams and contests in detail; 17 | + A profile page to view and edit user info; 18 | + Rank lists for exam and contest; 19 | + Multiple tabs and links on home page; 20 | + Supporting in-site announcements; 21 | + A page to view submissions with details of every test case and the submitted code; 22 | + All submissions on this site is visible in status page with a filter supported, if strict mode is disabled; 23 | + A strict mode user interface blocking every other pages except exam and contest. 24 | 25 | ## Recommended IDE Setup 26 | 27 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 28 | 29 | ## Customize configuration 30 | 31 | See [Vite Configuration Reference](https://vitejs.dev/config/). 32 | 33 | ## Project Setup 34 | 35 | ```sh 36 | npm install 37 | ``` 38 | 39 | ### Compile and Hot-Reload for Development 40 | 41 | ```sh 42 | npm run dev 43 | ``` 44 | 45 | ### Compile and Minify for Production 46 | 47 | ```sh 48 | npm run build 49 | ``` 50 | 51 | ### Lint with [ESLint](https://eslint.org/) 52 | 53 | ```sh 54 | npm run lint 55 | ``` 56 | 57 | ## Compatible Back End 58 | 59 | [ArcOJ-BackEnd](https://github.com/ArcanusNEO/ArcOJ-BackEnd) 60 | -------------------------------------------------------------------------------- /assets/nkuoj-favicon.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/nkuoj-favicon.ai -------------------------------------------------------------------------------- /assets/nkuoj-slogan.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/nkuoj-slogan.ai -------------------------------------------------------------------------------- /assets/readme-resources/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/readme-resources/admin.png -------------------------------------------------------------------------------- /assets/readme-resources/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/readme-resources/home.png -------------------------------------------------------------------------------- /assets/readme-resources/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/readme-resources/login.png -------------------------------------------------------------------------------- /assets/readme-resources/submission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/assets/readme-resources/submission.png -------------------------------------------------------------------------------- /assets/readme.md: -------------------------------------------------------------------------------- 1 | ### Global Assets 2 | 3 | **Note: assets under this directory is excluded from the project.** 4 | 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | NKU-OJ 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nkuoj-front-end", 3 | "version": "4.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore" 10 | }, 11 | "dependencies": { 12 | "@highlightjs/vue-plugin": "^2.1.0", 13 | "axios": "^1.3.2", 14 | "bootstrap": "^5.2.3", 15 | "highlight.js": "^11.7.0", 16 | "markdown-it": "^12.3.2", 17 | "markdown-it-mathjax3": "^4.3.2", 18 | "node-forge": "^1.3.1", 19 | "pinia": "^2.0.28", 20 | "vue": "^3.2.45", 21 | "vue-final-modal": "^4.0.3", 22 | "vue-router": "^4.1.6" 23 | }, 24 | "devDependencies": { 25 | "@vitejs/plugin-vue": "^4.0.0", 26 | "eslint": "^8.22.0", 27 | "eslint-plugin-vue": "^9.3.0", 28 | "vite": "^4.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/favicon-tju.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/public/favicon-tju.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/public/favicon.ico -------------------------------------------------------------------------------- /public/main.tju.css: -------------------------------------------------------------------------------- 1 | .table-success-stripped { 2 | background: repeating-linear-gradient( 3 | 135deg, 4 | #d1e7dd, 5 | #d1e7dd 10px, 6 | transparent 10px, 7 | transparent 20px 8 | ); 9 | } 10 | 11 | .btn-outline-purple { 12 | --bs-btn-color: #005187; 13 | --bs-btn-border-color: #005187; 14 | --bs-btn-hover-color: #fff; 15 | --bs-btn-hover-bg: #005187; 16 | --bs-btn-hover-border-color: #005187; 17 | --bs-btn-focus-shadow-rgb: 25,135,84; 18 | --bs-btn-active-color: #fff; 19 | --bs-btn-active-bg: #005187; 20 | --bs-btn-active-border-color: #005187; 21 | --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 22 | --bs-btn-disabled-color: #005187; 23 | --bs-btn-disabled-bg: transparent; 24 | --bs-btn-disabled-border-color: #005187; 25 | --bs-gradient: none; 26 | } 27 | .text-purple { 28 | color: #005187 !important; 29 | } 30 | .btn-rounded-purple { 31 | width: 56px; 32 | height: 56px; 33 | border-radius: 28px; 34 | --bs-btn-color: #005187; 35 | --bs-btn-border-color: none; 36 | --bs-btn-hover-color: #fff; 37 | --bs-btn-hover-bg: #005187; 38 | --bs-btn-hover-border-color: #005187; 39 | --bs-btn-focus-shadow-rgb: 25,135,84; 40 | --bs-btn-active-color: #fff; 41 | --bs-btn-active-bg: #005187; 42 | --bs-btn-active-border-color: #005187; 43 | --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 44 | --bs-btn-disabled-color: #005187; 45 | --bs-btn-disabled-bg: transparent; 46 | --bs-btn-disabled-border-color: #005187; 47 | --bs-gradient: none; 48 | transition: width 0.3s; 49 | text-align: center; 50 | } 51 | .btn-rounded-purple:hover { 52 | width: 105px; 53 | } 54 | .btn-rounded-purple:hover span ::after { 55 | display: inline; 56 | } 57 | .btn-rounded-purple span ::after { 58 | display: none; 59 | } 60 | .btn-rounded-purple span span { 61 | font-size: 16px; 62 | } 63 | .oj-pattern-background { 64 | position: absolute; 65 | min-width: 100%; 66 | min-height: 100%; 67 | top: 0; 68 | left: 0; 69 | overflow-y: auto; 70 | background: url("/pattern-light.svg"); 71 | } 72 | .list-group-item-transparent { 73 | background: transparent; 74 | } 75 | .clickable-card { 76 | height: 120px; 77 | background-color: #f8f9fa; 78 | transition: box-shadow 0.3s, background-color 0.3s; 79 | } 80 | .clickable-card:hover { 81 | background-color: #eff0f1; 82 | box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .05) !important; 83 | } 84 | .page-title { 85 | font-size: x-large; 86 | } 87 | .vertical-separator { 88 | height: 100px; 89 | width: 2px; 90 | background-color: #005187; 91 | } 92 | .pagination-purple { 93 | --bs-pagination-padding-x: .75rem; 94 | --bs-pagination-padding-y: .375rem; 95 | --bs-pagination-font-size: 1rem; 96 | --bs-pagination-color: #005187; 97 | --bs-pagination-bg: #fff; 98 | --bs-pagination-border-width: 1px; 99 | --bs-pagination-border-color: #dee2e6; 100 | --bs-pagination-border-radius: .375rem; 101 | --bs-pagination-hover-color: #004980; 102 | --bs-pagination-hover-bg: #e9ecef; 103 | --bs-pagination-hover-border-color: #dee2e6; 104 | --bs-pagination-focus-color: #004980; 105 | --bs-pagination-focus-bg: #e9ecef; 106 | --bs-pagination-focus-box-shadow: 0 0 0 .25rem rgba(0, 49, 80, .25); 107 | --bs-pagination-active-color: #fff; 108 | --bs-pagination-active-bg: #005187; 109 | --bs-pagination-active-border-color: #005187; 110 | --bs-pagination-disabled-color: #6c757d; 111 | --bs-pagination-disabled-bg: #fff; 112 | --bs-pagination-disabled-border-color: #dee2e6; 113 | list-style: none; 114 | } 115 | .form-control:focus { 116 | color:#212529; 117 | background-color:#fff; 118 | border-color:#004980; 119 | outline:0; 120 | box-shadow:0 0 0 .25rem rgba(0, 49, 80, .25) 121 | } 122 | .btn-close:focus { 123 | outline:0; 124 | box-shadow:0 0 0 .25rem rgba(0, 49, 80, .25); 125 | opacity:1 126 | } 127 | .dropdown-menu { 128 | --bs-dropdown-link-active-bg: #004980; 129 | } 130 | .form-select:focus { 131 | border-color:#004980; 132 | outline:0; 133 | box-shadow:0 0 0 .25rem rgba(0, 49, 80, .25) 134 | } 135 | .form-check-input:focus { 136 | border-color:#004980; 137 | outline:0; 138 | box-shadow:0 0 0 .25rem rgba(0, 49, 80, .25) 139 | } 140 | .form-check-input:checked { 141 | background-color: #005187; 142 | border-color: #005187; 143 | } 144 | .cursor-pointer { 145 | cursor: pointer; 146 | } 147 | -------------------------------------------------------------------------------- /public/nkuoj-slogan-light.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/nkuoj-slogan.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/qq-group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NankaiACM/NKUOJ-Front-End/1ccc302bce20b5980c209b09a1be7dc02783d3d2/public/qq-group.jpg -------------------------------------------------------------------------------- /public/tjuoj-slogan-light.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/tjuoj-slogan.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | .table-success-stripped { 2 | background: repeating-linear-gradient( 3 | 135deg, 4 | #d1e7dd, 5 | #d1e7dd 10px, 6 | transparent 10px, 7 | transparent 20px 8 | ); 9 | } 10 | 11 | .btn-outline-purple { 12 | --bs-btn-color: #923262; 13 | --bs-btn-border-color: #923262; 14 | --bs-btn-hover-color: #fff; 15 | --bs-btn-hover-bg: #923262; 16 | --bs-btn-hover-border-color: #923262; 17 | --bs-btn-focus-shadow-rgb: 25,135,84; 18 | --bs-btn-active-color: #fff; 19 | --bs-btn-active-bg: #923262; 20 | --bs-btn-active-border-color: #923262; 21 | --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 22 | --bs-btn-disabled-color: #923262; 23 | --bs-btn-disabled-bg: transparent; 24 | --bs-btn-disabled-border-color: #923262; 25 | --bs-gradient: none; 26 | } 27 | .text-purple { 28 | color: #923262 !important; 29 | } 30 | .btn-rounded-purple { 31 | width: 56px; 32 | height: 56px; 33 | border-radius: 28px; 34 | --bs-btn-color: #923262; 35 | --bs-btn-border-color: none; 36 | --bs-btn-hover-color: #fff; 37 | --bs-btn-hover-bg: #923262; 38 | --bs-btn-hover-border-color: #923262; 39 | --bs-btn-focus-shadow-rgb: 25,135,84; 40 | --bs-btn-active-color: #fff; 41 | --bs-btn-active-bg: #923262; 42 | --bs-btn-active-border-color: #923262; 43 | --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 44 | --bs-btn-disabled-color: #923262; 45 | --bs-btn-disabled-bg: transparent; 46 | --bs-btn-disabled-border-color: #923262; 47 | --bs-gradient: none; 48 | transition: width 0.3s; 49 | text-align: center; 50 | } 51 | .btn-rounded-purple:hover { 52 | width: 105px; 53 | } 54 | .btn-rounded-purple:hover span ::after { 55 | display: inline; 56 | } 57 | .btn-rounded-purple span ::after { 58 | display: none; 59 | } 60 | .btn-rounded-purple span span { 61 | font-size: 16px; 62 | } 63 | .oj-pattern-background { 64 | position: absolute; 65 | min-width: 100%; 66 | min-height: 100%; 67 | top: 0; 68 | left: 0; 69 | overflow-y: auto; 70 | background: url("/pattern-light.svg"); 71 | } 72 | .list-group-item-transparent { 73 | background: transparent; 74 | } 75 | .clickable-card { 76 | height: 120px; 77 | background-color: #f8f9fa; 78 | transition: box-shadow 0.3s, background-color 0.3s; 79 | } 80 | .clickable-card:hover { 81 | background-color: #eff0f1; 82 | box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .05) !important; 83 | } 84 | .page-title { 85 | font-size: x-large; 86 | } 87 | .vertical-separator { 88 | height: 100px; 89 | width: 2px; 90 | background-color: #923262; 91 | } 92 | .pagination-purple { 93 | --bs-pagination-padding-x: .75rem; 94 | --bs-pagination-padding-y: .375rem; 95 | --bs-pagination-font-size: 1rem; 96 | --bs-pagination-color: #892959; 97 | --bs-pagination-bg: #fff; 98 | --bs-pagination-border-width: 1px; 99 | --bs-pagination-border-color: #dee2e6; 100 | --bs-pagination-border-radius: .375rem; 101 | --bs-pagination-hover-color: #892959; 102 | --bs-pagination-hover-bg: #e9ecef; 103 | --bs-pagination-hover-border-color: #dee2e6; 104 | --bs-pagination-focus-color: #892959; 105 | --bs-pagination-focus-bg: #e9ecef; 106 | --bs-pagination-focus-box-shadow: 0 0 0 .25rem rgba(80, 20, 50, .25); 107 | --bs-pagination-active-color: #fff; 108 | --bs-pagination-active-bg: #923262; 109 | --bs-pagination-active-border-color: #923262; 110 | --bs-pagination-disabled-color: #6c757d; 111 | --bs-pagination-disabled-bg: #fff; 112 | --bs-pagination-disabled-border-color: #dee2e6; 113 | list-style: none; 114 | } 115 | .form-control:focus { 116 | color:#212529; 117 | background-color:#fff; 118 | border-color:#a24272; 119 | outline:0; 120 | box-shadow:0 0 0 .25rem rgba(80, 20, 50, .25) 121 | } 122 | .btn-close:focus { 123 | outline:0; 124 | box-shadow:0 0 0 .25rem rgba(80, 20, 50, .25); 125 | opacity:1 126 | } 127 | .dropdown-menu { 128 | --bs-dropdown-link-active-bg: #a24272; 129 | } 130 | .form-select:focus { 131 | border-color:#a24272; 132 | outline:0; 133 | box-shadow:0 0 0 .25rem rgba(80, 20, 50, .25) 134 | } 135 | .form-check-input:focus { 136 | border-color:#a24272; 137 | outline:0; 138 | box-shadow:0 0 0 .25rem rgba(80, 20, 50, .25) 139 | } 140 | .form-check-input:checked { 141 | background-color: #923262; 142 | border-color: #923262; 143 | } 144 | .cursor-pointer { 145 | cursor: pointer; 146 | } 147 | -------------------------------------------------------------------------------- /src/components/admin/CardManageProblem.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/components/admin/CardRanking.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/components/admin/CardRejudge.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /src/components/admin/CardViewProblemSubmissions.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/headbar/HeadBarAdmin.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 73 | 74 | 80 | -------------------------------------------------------------------------------- /src/components/headbar/HeadBarPublic.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 51 | 52 | 58 | -------------------------------------------------------------------------------- /src/components/headbar/HeadBarStrict.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 85 | 86 | 92 | -------------------------------------------------------------------------------- /src/components/headbar/HeadBarUniversal.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 98 | 99 | 105 | -------------------------------------------------------------------------------- /src/components/icons/IconArrowClockwise.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconArrowClockwiseSmall.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconBoxArrowInUpRightSmall.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconCheck2Circle.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconCheckAll.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconChevronDoubleRightSmall.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconClipboardCheck.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/icons/IconClock.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconClockHistory.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/icons/IconCodeSquare.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconDownloadSmall.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconEasel.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconFileXLarge.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconFunnelFillSmall.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconFunnelSmall.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconHouse.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconJournalCode.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/icons/IconJournalPlus.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/components/icons/IconJump.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/components/icons/IconLightBulbSmall.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconListColumnsReverse.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconListOL.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconMegaphone.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconPDFLarge.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconPencilSquare.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconPerson.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconReception.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconReplySmall.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconSend.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconSendCheck.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconSliders.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconTrophy.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/icons/IconUIChecks.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/icons/IconUIChecksGrid.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/modal/ModalAddContestExam.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 136 | 137 | 140 | -------------------------------------------------------------------------------- /src/components/modal/ModalAddCourse.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 89 | 90 | 93 | -------------------------------------------------------------------------------- /src/components/modal/ModalAddCoursePasscode.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /src/components/modal/ModalAdminIndexProblem.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /src/components/modal/ModalAdminIndexProblemSet.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /src/components/modal/ModalAdminProblemNew.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 87 | 88 | 91 | -------------------------------------------------------------------------------- /src/components/modal/ModalAdminSelectProblem.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 72 | 73 | 76 | -------------------------------------------------------------------------------- /src/components/modal/ModalBase.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 44 | 45 | 57 | -------------------------------------------------------------------------------- /src/components/modal/ModalCompilationOutput.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 45 | 46 | 58 | -------------------------------------------------------------------------------- /src/components/modal/ModalConfirmBox.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 68 | 69 | 81 | -------------------------------------------------------------------------------- /src/components/modal/ModalFilter.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 61 | 62 | 65 | -------------------------------------------------------------------------------- /src/components/modal/ModalJumpToSubmission.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 45 | 46 | 49 | -------------------------------------------------------------------------------- /src/components/modal/ModalLogin.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 134 | 135 | 138 | -------------------------------------------------------------------------------- /src/components/modal/ModalMsgBox.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 47 | 48 | -------------------------------------------------------------------------------- /src/components/modal/ModalMySubmissions.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 81 | 82 | 85 | -------------------------------------------------------------------------------- /src/components/modal/ModalPreviewMarkdown.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | 44 | 53 | -------------------------------------------------------------------------------- /src/components/modal/ModalRejudgeProgressing.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 52 | 53 | 56 | -------------------------------------------------------------------------------- /src/components/modal/ModalSubmissionStatusTracing.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 106 | 107 | 110 | -------------------------------------------------------------------------------- /src/components/modal/ModalUpdateProfile.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 95 | 96 | 99 | -------------------------------------------------------------------------------- /src/components/panels/PanelAssignment.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 56 | 57 | 60 | -------------------------------------------------------------------------------- /src/components/panels/PanelBulletin.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /src/components/panels/PanelContestExam.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 106 | 107 | 110 | -------------------------------------------------------------------------------- /src/components/panels/PanelCourse.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 69 | 70 | 73 | -------------------------------------------------------------------------------- /src/components/panels/PanelJump.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/components/panels/PanelStrictContest.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 79 | 80 | 83 | -------------------------------------------------------------------------------- /src/components/panels/PanelStrictExam.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 81 | 82 | 85 | -------------------------------------------------------------------------------- /src/components/problem/ButtonSubmit.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /src/components/problem/CardProblemContent.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 83 | 84 | 89 | -------------------------------------------------------------------------------- /src/components/problem/DropdownNavigator.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 57 | 58 | 61 | -------------------------------------------------------------------------------- /src/components/submission/CardCode.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 76 | 77 | 80 | -------------------------------------------------------------------------------- /src/components/submission/CardTestCase.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 70 | 71 | 80 | -------------------------------------------------------------------------------- /src/components/wrapper/CardProgressBar.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 83 | 84 | 87 | -------------------------------------------------------------------------------- /src/components/wrapper/Pagination.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 87 | 88 | 91 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | publicPageEnabled: false 3 | } 4 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | import { createVfm } from 'vue-final-modal' 4 | 5 | import App from './App.vue' 6 | import router from './router' 7 | 8 | import 'bootstrap/dist/css/bootstrap.min.css' 9 | import 'bootstrap/dist/js/bootstrap.min' 10 | 11 | import 'vue-final-modal/style.css' 12 | 13 | import 'highlight.js/styles/stackoverflow-light.css' 14 | import hljs from 'highlight.js/lib/core'; 15 | import hljsVuePlugin from "@highlightjs/vue-plugin"; 16 | import hljs_javascript from 'highlight.js/lib/languages/javascript'; 17 | import hljs_cpp from 'highlight.js/lib/languages/cpp'; 18 | import hljs_c from 'highlight.js/lib/languages/c'; 19 | import hljs_go from 'highlight.js/lib/languages/go'; 20 | import hljs_python from 'highlight.js/lib/languages/python'; 21 | hljs.registerLanguage('javascript', hljs_javascript); 22 | hljs.registerLanguage('cpp', hljs_cpp); 23 | hljs.registerLanguage('c', hljs_c); 24 | hljs.registerLanguage('go', hljs_go); 25 | hljs.registerLanguage('python', hljs_python); 26 | 27 | import './assets/main.css' 28 | 29 | const app = createApp(App) 30 | const pinia = createPinia() 31 | const vfm = createVfm() 32 | 33 | // configure persistent storage with localstorage 34 | pinia.use((context) => { 35 | const storeId = context.store.$id 36 | const serializer = { 37 | serialize: JSON.stringify, 38 | deserialize: JSON.parse 39 | }; 40 | const fromStorage = serializer.deserialize(window.localStorage.getItem(storeId)); 41 | if (fromStorage) { 42 | context.store.$patch(fromStorage); 43 | } 44 | context.store.$subscribe((mutation, state) => { 45 | window.localStorage.setItem(storeId, serializer.serialize(state)); 46 | }); 47 | }); 48 | 49 | app.use(pinia); 50 | app.use(vfm); 51 | app.use(router); 52 | app.use(hljsVuePlugin); 53 | 54 | app.mount('#app'); 55 | -------------------------------------------------------------------------------- /src/pages/AdminHomePage.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/pages/AdminProblemDataPage.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 91 | 92 | 95 | -------------------------------------------------------------------------------- /src/pages/AdminProblemSpecialJudgePage.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 112 | 113 | 116 | -------------------------------------------------------------------------------- /src/pages/AlgoAssociationPage.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /src/pages/BulletinContentPage.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 47 | 48 | 57 | -------------------------------------------------------------------------------- /src/pages/BulletinPage.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 52 | 53 | 56 | -------------------------------------------------------------------------------- /src/pages/ContestRankingPage.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/pages/ExamRankingPage.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /src/pages/HomePage.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/pages/LogoutPage.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | 45 | 53 | -------------------------------------------------------------------------------- /src/pages/NotFoundPage.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 31 | 32 | 40 | -------------------------------------------------------------------------------- /src/pages/ProblemListPage.vue: -------------------------------------------------------------------------------- 1 | 35 | 83 | 89 | -------------------------------------------------------------------------------- /src/pages/ProblemPage.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 116 | 117 | 120 | -------------------------------------------------------------------------------- /src/pages/ProfilePage.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 98 | 99 | 102 | -------------------------------------------------------------------------------- /src/pages/StrictHomePage.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 112 | 113 | 116 | -------------------------------------------------------------------------------- /src/pages/SubmissionPage.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 118 | 119 | 122 | -------------------------------------------------------------------------------- /src/stores/filter-data.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useFilterDataStore = defineStore('filter-data', () => { 5 | const uid = ref(null) 6 | const pid = ref(null) 7 | const nickname = ref(null) 8 | 9 | function setUID(v) { 10 | uid.value = v 11 | } 12 | 13 | function setPID(v) { 14 | pid.value = v 15 | } 16 | 17 | function setNickname(v) { 18 | nickname.value = v; 19 | } 20 | 21 | return { uid, pid, nickname, setUID, setPID, setNickname } 22 | }) 23 | -------------------------------------------------------------------------------- /src/stores/strict-mode.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useStrictModeStore = defineStore('strict-mode', () => { 5 | const clientStrictMode = ref(false) 6 | const serverStrictMode = ref(false) 7 | 8 | function setClientStrictMode(mode) { 9 | clientStrictMode.value = mode 10 | } 11 | 12 | function setServerStrictMode(mode) { 13 | serverStrictMode.value = mode 14 | } 15 | 16 | return { clientStrictMode, serverStrictMode, setClientStrictMode, setServerStrictMode } 17 | }) 18 | -------------------------------------------------------------------------------- /src/stores/user-data.js: -------------------------------------------------------------------------------- 1 | import {computed, ref} from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useUserDataStore = defineStore('user-data', () => { 5 | const uid = ref(0) 6 | const username = ref('') 7 | const nickname = ref('') 8 | const valid = ref(false) 9 | const permission = ref(0) 10 | 11 | function setUID(newUID) { 12 | uid.value = newUID 13 | } 14 | 15 | function setUsername(newUsername) { 16 | username.value = newUsername 17 | } 18 | 19 | function setNickname(newNickname) { 20 | nickname.value = newNickname 21 | } 22 | 23 | function setPermission(permissionLevel) { 24 | permission.value = permissionLevel 25 | } 26 | 27 | function setValid() { 28 | valid.value = true 29 | } 30 | 31 | function clear() { 32 | valid.value = false 33 | nickname.value = '' 34 | username.value = '' 35 | uid.value = 0 36 | permission.value = 0 37 | } 38 | 39 | const isAdministrator = computed(() => permission.value > 0) 40 | 41 | return { uid, username, nickname, valid, permission, setUID, setUsername, setNickname, setPermission, setValid, clear, isAdministrator } 42 | }) 43 | -------------------------------------------------------------------------------- /src/templates/AppAdmin.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 72 | 73 | 75 | -------------------------------------------------------------------------------- /src/templates/AppPublic.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /src/templates/AppStrict.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 69 | 70 | 72 | -------------------------------------------------------------------------------- /src/templates/AppUniversal.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 71 | 72 | 74 | -------------------------------------------------------------------------------- /src/util/compile-markdown.js: -------------------------------------------------------------------------------- 1 | import markdownIt from 'markdown-it'; 2 | import markdownItMathjax from 'markdown-it-mathjax3'; 3 | 4 | export const compileMarkdown = function (content) { 5 | const markdownItObject = markdownIt({ 6 | html: true, 7 | linkify: true, 8 | typographer: true 9 | }); 10 | markdownItObject.use(markdownItMathjax); 11 | return markdownItObject.render(content); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/util/date-to-str.js: -------------------------------------------------------------------------------- 1 | export default function (dateString) { 2 | // convert from UTC+0 to UTC+8 3 | return new Date(Date.parse(dateString)).toLocaleString() 4 | } 5 | -------------------------------------------------------------------------------- /src/util/encrypt.js: -------------------------------------------------------------------------------- 1 | import forge from 'node-forge' 2 | 3 | export default { 4 | encrypt: (message) => { 5 | const publicKey = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Pft9UagQd0qms5MSWP46oTVUCrckjW40mAT5HUqHqmhPM/TcUi7UzLJHj9NAosEDU7u+4if5dCjx31B6ZAOiDdeS6ahRSYgFyxdmIBrMXMbdWdviQ5FThTcOc9ppKSpAfRi3nexqmSi5Kc2RUDTUFIKXvCheciEAyT5/XFxsDKuBPzOzHgrhvEkv0SnyaRWdBZoExuLT4EeGAepyq4Otf8+qw8oSySFzV1qCSCC0fVSHmOQGMbg3RrBNBPqPUpLVVm+wqEHBGLHi94kA/vSObgRZVOPr2P2RpcDh6Z36g5PqaKBtC+vKnWokeDzO85QHgSNcapfCO92MlvV94lPlwIDAQAB\n-----END PUBLIC KEY-----' 6 | const keyObject = forge.pki.publicKeyFromPem(publicKey) 7 | const encrypted = keyObject.encrypt(message) 8 | return forge.util.encode64(encrypted) 9 | } 10 | } -------------------------------------------------------------------------------- /src/util/http-code-to-str.js: -------------------------------------------------------------------------------- 1 | export default function (code) { 2 | const codeWithStr = { 3 | 298: '[298] 用户名或密码错误', 4 | 401: '[401] 未授权', 5 | 440: '[440] 登录超时', 6 | 297: '[297] 验证码错误', 7 | 296: '[296] 用户已经存在', 8 | 299: '[299] 表单校验错误', 9 | 500: '[500] 服务器内部错误', 10 | 404: '[404] 不存在', 11 | 400: '[400] 请求有误', 12 | 403: '[403] 没有权限', 13 | 201: '[201] 已创建', 14 | 501: '[501] 未实现', 15 | 502: '[502] 网关错误', 16 | 503: '[503] 服务不可用', 17 | 504: '[504] 请求超时', 18 | 413: '[413] 请求过大', 19 | 425: '[425] 太早了', 20 | 200: '[200] 好', 21 | 100: '[100] 继续', 22 | } 23 | 24 | if (!(code in codeWithStr)) { 25 | return `[${code}] 未知错误` 26 | } 27 | return codeWithStr[code] 28 | } 29 | -------------------------------------------------------------------------------- /src/util/lang-code-to-text.js: -------------------------------------------------------------------------------- 1 | export default function (code) { 2 | const codeText = { 3 | 0: 'C', 4 | 1: 'C++', 5 | 2: 'Python', 6 | 3: 'Javascript', 7 | 4: 'Go', 8 | 5: 'Text', 9 | 6: 'Pypy3', 10 | } 11 | 12 | if (!(code in codeText)) { 13 | return `[${code}] 未知语言` 14 | } 15 | return codeText[code] 16 | } 17 | -------------------------------------------------------------------------------- /src/util/ordinal-number-to-str.js: -------------------------------------------------------------------------------- 1 | export default function (num) { 2 | let s = '', t; 3 | while (num > 0) { 4 | t = (num - 1) % 26; 5 | s = String.fromCharCode(65 + t) + s; 6 | num = (num - t) / 26 | 0; 7 | } 8 | return s || undefined; 9 | } 10 | -------------------------------------------------------------------------------- /src/util/status-code-to-str-short.js: -------------------------------------------------------------------------------- 1 | export default function (code) { 2 | const codeText = { 3 | 99: 'QUE', 4 | 100: 'PROC', 5 | 107: 'AC', 6 | 0: 'AC', 7 | 1: 'PE', 8 | 102: 'WA', 9 | 2: 'WA', 10 | 108: 'AC*', 11 | 101: 'CE', 12 | 103: 'RE', 13 | 4: 'RE', 14 | 105: 'TLE', 15 | 6: 'TLE', 16 | 104: 'MLE', 17 | 5: 'MLE', 18 | 106: 'OLE', 19 | 7: 'OLE', 20 | 109: 'FLE', 21 | 118: 'SE', 22 | 98: 'PROC', 23 | 110: 'MD', 24 | 121: 'HID', 25 | } 26 | 27 | if (!(code in codeText)) { 28 | return `UNK` 29 | } 30 | return codeText[code] 31 | } 32 | -------------------------------------------------------------------------------- /src/util/status-code-to-str.js: -------------------------------------------------------------------------------- 1 | export default function (code) { 2 | const codeText = { 3 | 99: '排队中', 4 | 100: '评测中', 5 | 107: '答案正确', 6 | 0: '答案正确', 7 | 1: '答案正确*', 8 | 102: '答案错误', 9 | 2: '答案错误', 10 | 108: '答案正确*', 11 | 101: '编译失败', 12 | 103: '运行出错', 13 | 4: '运行出错', 14 | 105: '运行超时', 15 | 6: '运行超时', 16 | 104: '内存超限', 17 | 5: '内存超限', 18 | 106: '输出超限', 19 | 7: '输出超限', 20 | 109: '函数超限', 21 | 118: '系统错误', 22 | 98: '正在编译', 23 | 110: '多组数据', 24 | 121: '被隐藏', 25 | } 26 | 27 | if (!(code in codeText)) { 28 | return `[${code}] 未知状态` 29 | } 30 | return codeText[code] 31 | } 32 | -------------------------------------------------------------------------------- /src/util/status-code-to-variant-str.js: -------------------------------------------------------------------------------- 1 | export default function (code) { 2 | const today = new Date(); 3 | const isFoolsDay = today.getMonth() + 1 === 4 && today.getDate() === 1; 4 | const codeVariant = { 5 | 99: 'secondary', 6 | 100: 'secondary', 7 | 107: isFoolsDay ? 'danger' : 'success', 8 | 0: isFoolsDay ? 'danger' : 'success', 9 | 1: isFoolsDay ? 'danger' : 'success', 10 | 102: isFoolsDay ? 'success' : 'danger', 11 | 2: isFoolsDay ? 'success' : 'danger', 12 | 108: isFoolsDay ? 'danger' : 'success', 13 | 101: 'warning', 14 | 103: 'primary', 15 | 105: 'info', 16 | 6: 'info', 17 | 4: 'primary', 18 | 5: 'info', 19 | 104: 'info', 20 | 106: 'dark', 21 | 7: 'dark', 22 | 109: 'dark', 23 | 118: 'dark', 24 | 110: 'dark', 25 | 98: 'secondary', 26 | 121: 'dark', 27 | } 28 | 29 | if (!(code in codeVariant)) { 30 | return 'dark' 31 | } 32 | return codeVariant[code] 33 | } 34 | -------------------------------------------------------------------------------- /src/util/uid-to-str.js: -------------------------------------------------------------------------------- 1 | export default function (uid) { 2 | return String(uid).padStart(5, '0') 3 | } 4 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [vue()], 9 | resolve: { 10 | alias: { 11 | '@': fileURLToPath(new URL('./src', import.meta.url)) 12 | } 13 | } 14 | }) 15 | --------------------------------------------------------------------------------