From 85c2f963771027c689c95530f61b366e81829710 Mon Sep 17 00:00:00 2001 From: Himit_ZH <372347736@qq.com> Date: Mon, 6 Dec 2021 21:04:45 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=AD=E7=BB=83=E6=A8=A1=E5=9D=9780%?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=EF=BC=8C=E4=BC=98=E5=8C=96=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E6=8E=92=E8=A1=8C=E6=A6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/.vuepress/config.js | 32 +- docs/docs/README.md | 2 +- docs/docs/deploy/docker.md | 120 ----- .../docs/{use => deploy}/multi-judgeserver.md | 0 docs/docs/deploy/open-https.md | 35 ++ docs/docs/deploy/update.md | 99 ++++ docs/docs/{deploy => monomer}/backend.md | 0 docs/docs/{deploy => monomer}/frontend.md | 0 docs/docs/{deploy => monomer}/judgeserver.md | 1 + .../docs/{deploy => monomer}/mysql-checker.md | 0 docs/docs/{deploy => monomer}/mysql.md | 0 docs/docs/{deploy => monomer}/nacos.md | 0 docs/docs/{deploy => monomer}/redis.md | 0 docs/docs/{deploy => monomer}/rsync.md | 0 docs/docs/use/close-free-cdn.md | 195 +++++++ .../admin/AdminContestController.java | 27 +- .../admin/AdminTrainingController.java | 77 +-- .../hoj/controller/oj/CommentController.java | 18 +- .../hoj/controller/oj/ContestController.java | 22 +- .../hoj/controller/oj/JudgeController.java | 9 +- .../top/hcode/hoj/dao/TrainingMapper.java | 9 +- .../java/top/hcode/hoj/dao/xml/AuthMapper.xml | 5 - .../top/hcode/hoj/dao/xml/TrainingMapper.xml | 46 +- .../hoj/dao/xml/TrainingProblemMapper.xml | 2 +- .../top/hcode/hoj/pojo/vo/TrainingVo.java | 7 +- .../contest/impl/ContestServiceImpl.java | 7 +- .../hoj/service/training/TrainingService.java | 5 + .../impl/TrainingProblemServiceImpl.java | 22 +- .../training/impl/TrainingServiceImpl.java | 51 +- .../task/Impl/CodeForcesJudge.java | 28 +- .../hoj/pojo/entity/contest/Contest.java | 3 + .../hoj/pojo/entity/training/Training.java | 1 + .../pojo/entity/training/TrainingProblem.java | 1 + hoj-vue/src/App.vue | 27 +- hoj-vue/src/common/api.js | 223 +++++++- hoj-vue/src/common/constants.js | 18 +- ...estAddProblem.vue => AddPublicProblem.vue} | 94 ++-- hoj-vue/src/components/oj/common/NavBar.vue | 18 + .../src/components/oj/common/Pagination.vue | 7 +- hoj-vue/src/i18n/admin/en-US.js | 53 +- hoj-vue/src/i18n/admin/zh-CN.js | 44 +- hoj-vue/src/i18n/oj/en-US.js | 1 + hoj-vue/src/i18n/oj/zh-CN.js | 1 + hoj-vue/src/router/adminRoutes.js | 38 +- hoj-vue/src/router/ojRoutes.js | 111 ++-- hoj-vue/src/store/index.js | 4 +- hoj-vue/src/store/training.js | 108 ++++ hoj-vue/src/views/admin/Home.vue | 82 ++- hoj-vue/src/views/admin/contest/Contest.vue | 21 +- .../src/views/admin/problem/ProblemList.vue | 10 +- hoj-vue/src/views/admin/training/Category.vue | 190 +++++++ hoj-vue/src/views/admin/training/Training.vue | 250 +++++++++ .../src/views/admin/training/TrainingList.vue | 235 +++++++++ .../admin/training/TrainingProblemList.vue | 426 +++++++++++++++ .../src/views/oj/contest/ContestDetails.vue | 7 +- .../oj/contest/children/ACMContestRank.vue | 116 +++-- .../oj/contest/children/OIContestRank.vue | 4 + hoj-vue/src/views/oj/problem/Problem.vue | 25 +- .../src/views/oj/status/SubmissionList.vue | 5 +- .../src/views/oj/training/TrainingDetails.vue | 234 +++++++++ .../src/views/oj/training/TrainingList.vue | 279 ++++++++++ .../views/oj/training/TrainingProblemList.vue | 172 +++++++ .../src/views/oj/training/TrainingRank.vue | 484 ++++++++++++++++++ hoj-vue/vue.config.js | 2 +- sqlAndsetting/hoj-update.sql | 78 ++- sqlAndsetting/hoj.sql | 10 +- 66 files changed, 3782 insertions(+), 419 deletions(-) rename docs/docs/{use => deploy}/multi-judgeserver.md (100%) create mode 100644 docs/docs/deploy/open-https.md create mode 100644 docs/docs/deploy/update.md rename docs/docs/{deploy => monomer}/backend.md (100%) rename docs/docs/{deploy => monomer}/frontend.md (100%) rename docs/docs/{deploy => monomer}/judgeserver.md (99%) rename docs/docs/{deploy => monomer}/mysql-checker.md (100%) rename docs/docs/{deploy => monomer}/mysql.md (100%) rename docs/docs/{deploy => monomer}/nacos.md (100%) rename docs/docs/{deploy => monomer}/redis.md (100%) rename docs/docs/{deploy => monomer}/rsync.md (100%) create mode 100644 docs/docs/use/close-free-cdn.md delete mode 100644 hoj-springboot/DataBackup/src/main/java/top/hcode/hoj/dao/xml/AuthMapper.xml rename hoj-vue/src/components/admin/{ContestAddProblem.vue => AddPublicProblem.vue} (56%) create mode 100644 hoj-vue/src/store/training.js create mode 100644 hoj-vue/src/views/admin/training/Category.vue create mode 100644 hoj-vue/src/views/admin/training/Training.vue create mode 100644 hoj-vue/src/views/admin/training/TrainingList.vue create mode 100644 hoj-vue/src/views/admin/training/TrainingProblemList.vue create mode 100644 hoj-vue/src/views/oj/training/TrainingDetails.vue create mode 100644 hoj-vue/src/views/oj/training/TrainingList.vue create mode 100644 hoj-vue/src/views/oj/training/TrainingProblemList.vue create mode 100644 hoj-vue/src/views/oj/training/TrainingRank.vue diff --git a/docs/docs/.vuepress/config.js b/docs/docs/.vuepress/config.js index 09308025..f6165d16 100644 --- a/docs/docs/.vuepress/config.js +++ b/docs/docs/.vuepress/config.js @@ -22,24 +22,32 @@ module.exports = { title: '开始介绍', collapsable: true, children: [ - 'introducition/', - 'introducition/about', + 'introducition/' ] }, { - title: '部署文档', + title: '快速部署', collapsable: true, children: [ 'deploy/', 'deploy/docker', - 'deploy/mysql', - 'deploy/mysql-checker', - 'deploy/redis', - 'deploy/nacos', - 'deploy/backend', - 'deploy/judgeserver', - 'deploy/frontend', - 'deploy/rsync' + 'deploy/open-https', + 'deploy/multi-judgeserver', + 'deploy/update' + ] + }, + { + title: '单体部署', + collapsable: true, + children: [ + 'monomer/mysql', + 'monomer/mysql-checker', + 'monomer/redis', + 'monomer/nacos', + 'monomer/backend', + 'monomer/judgeserver', + 'monomer/frontend', + 'monomer/rsync' ] }, { @@ -63,8 +71,8 @@ module.exports = { 'use/admin-user', 'use/notice-announcement', 'use/discussion-admin', - 'use/multi-judgeserver', 'use/update-fe', + 'use/close-free-cdn', 'use/spj' ] }, diff --git a/docs/docs/README.md b/docs/docs/README.md index f876617b..3a6c1be4 100644 --- a/docs/docs/README.md +++ b/docs/docs/README.md @@ -16,5 +16,5 @@ features: details: 判题使用 cgroup 隔离用户程序,网站权限控制完善 - title: 多样化 details: 独有自身判题服务,同时支持其它知名OJ题目的提交判题 -footer: MIT Licensed | Copyright © 2021.11.11 @Author Himit_ZH QQ Group:598587305 +footer: MIT Licensed | Copyright © 2021.12.02 @Author Himit_ZH QQ Group:598587305 --- \ No newline at end of file diff --git a/docs/docs/deploy/docker.md b/docs/docs/deploy/docker.md index c86a8b1f..8d3a6435 100644 --- a/docs/docs/deploy/docker.md +++ b/docs/docs/deploy/docker.md @@ -282,123 +282,3 @@ Password: 开启SMTP服务后生成的随机授权码 > 提示:需要开启多台判题机,就如当前第4步的操作一样,在每台服务器上执行以上的操作即可。 5. 两个服务都启动完成,在浏览器输入主服务ip或域名进行访问,登录root账号到后台查看服务状态。 - - - -## 三、开启Https - -- 单机部署: - - 提供server.crt和server.key证书与密钥文件放置`/standAlone`目录下,与`docker-compose.yml`和`.env`文件放置同一位置,然后修改`docker-compose.yml`中的hoj-frontend的配置 - -- 分布式部署: - - 提供server.crt和server.key证书与密钥文件放置`/distributed/main目录下,与`docker-compose.yml`和`.env`文件放置同一位置,然后修改`docker-compose.yml`中的hoj-frontend的配置 - - - -```yaml -hoj-frontend: - image: registry.cn-shenzhen.aliyuncs.com/hcode/hoj_frontend - container_name: hoj-frontend - restart: always - # 开启https,请提供证书 - volumes: - - ./server.crt:/etc/nginx/etc/crt/server.crt - - ./server.key:/etc/nginx/etc/crt/server.key - # 修改前端logo - # - ./logo.a0924d7d.png:/usr/share/nginx/html/assets/img/logo.a0924d7d.png - # - ./backstage.8bce8c6e.png:/usr/share/nginx/html/assets/img/backstage.8bce8c6e.png - environment: - - SERVER_NAME=localhost # 提供你的域名!!!! - - BACKEND_SERVER_HOST=${BACKEND_HOST:-172.20.0.5} # backend后端服务地址 - - BACKEND_SERVER_PORT=${BACKEND_PORT:-6688} # backend后端服务端口号 - - USE_HTTPS=true # 使用https请设置为true - ports: - - "80:80" - - "443:443" - networks: - hoj-network: - ipv4_address: 172.20.0.6 -``` - -## 四、更新最新版本 - -> 2021.09.21之后部署hoj的请看下面操作 - -请在对应的docker-compose.yml当前文件夹下执行`docker-compose pull`拉取最新镜像,然后重新`docker-compose up -d`即可。 - - - -> 2021.09.21之前部署hoj的请看下面操作 - -### 1、修改MySQL8.0默认的密码加密方式 - -(1)进行hoj-mysql容器 - -```shell -docker exec -it hoj-mysql bash -``` - - (2) 输入对应的mysql密码,进入mysql数据库 - - 注意:-p 后面跟着数据库密码例如hoj123456 - -```shell -mysql -uroot -p数据库密码 -``` - -(3)成功进入后,执行以下命令 - -```shell -mysql> use mysql; - -mysql> grant all PRIVILEGES on *.* to root@'%' WITH GRANT OPTION; - - -mysql> ALTER user 'root'@'%' IDENTIFIED BY '数据库密码' PASSWORD EXPIRE NEVER; - - -mysql> ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '数据库密码'; - - -mysql> FLUSH PRIVILEGES; -``` - -(4) 两次exit 退出mysql和容器 - -### 2、 添加hoj-mysql-checker模块 - -(1)可以选择拉取仓库最新的docker-compose.yml文件(跟部署操作一样,但是会覆盖之前设置的参数)或者访问: - -https://gitee.com/himitzh0730/hoj-deploy/blob/master/standAlone/docker-compose.yml - -(2)或者编辑docker-compose.yml文件,手动添加新模块 - -```yaml - hoj-mysql-checker: - image: registry.cn-shenzhen.aliyuncs.com/hcode/hoj_database_checker - container_name: hoj-mysql-checker - depends_on: - - hoj-mysql - links: - - hoj-mysql:mysql - environment: - - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-hoj123456} - networks: - hoj-network: - ipv4_address: 172.20.0.8 -``` - -(3) 保存后重启容器即可 - -```shell -docker-compose down - -docker-compose pull - -docker-compose up -d -``` - - - diff --git a/docs/docs/use/multi-judgeserver.md b/docs/docs/deploy/multi-judgeserver.md similarity index 100% rename from docs/docs/use/multi-judgeserver.md rename to docs/docs/deploy/multi-judgeserver.md diff --git a/docs/docs/deploy/open-https.md b/docs/docs/deploy/open-https.md new file mode 100644 index 00000000..dba144b4 --- /dev/null +++ b/docs/docs/deploy/open-https.md @@ -0,0 +1,35 @@ +# 开启HTTPS + +- 单机部署: + + 提供server.crt和server.key证书与密钥文件放置`/standAlone`目录下,与`docker-compose.yml`和`.env`文件放置同一位置,然后修改`docker-compose.yml`中的hoj-frontend的配置 + +- 分布式部署: + + 提供server.crt和server.key证书与密钥文件放置`/distributed/main`目录下,与`docker-compose.yml`和`.env`文件放置同一位置,然后修改`docker-compose.yml`中的hoj-frontend的配置 + +```yaml +hoj-frontend: + image: registry.cn-shenzhen.aliyuncs.com/hcode/hoj_frontend + container_name: hoj-frontend + restart: always + # 开启https,请提供证书 + volumes: + - ./server.crt:/etc/nginx/etc/crt/server.crt + - ./server.key:/etc/nginx/etc/crt/server.key + # 修改前端logo + # - ./logo.a0924d7d.png:/usr/share/nginx/html/assets/img/logo.a0924d7d.png + # - ./backstage.8bce8c6e.png:/usr/share/nginx/html/assets/img/backstage.8bce8c6e.png + environment: + - SERVER_NAME=localhost # 提供你的域名!!!! + - BACKEND_SERVER_HOST=${BACKEND_HOST:-172.20.0.5} # backend后端服务地址 + - BACKEND_SERVER_PORT=${BACKEND_PORT:-6688} # backend后端服务端口号 + - USE_HTTPS=true # 使用https请设置为true + ports: + - "80:80" + - "443:443" + networks: + hoj-network: + ipv4_address: 172.20.0.6 +``` + diff --git a/docs/docs/deploy/update.md b/docs/docs/deploy/update.md new file mode 100644 index 00000000..b1e12419 --- /dev/null +++ b/docs/docs/deploy/update.md @@ -0,0 +1,99 @@ +# 如何更新 + +## 一、无二次开发的更新 + +> 2021.09.21之后部署hoj的请看下面操作 + +请在对应的docker-compose.yml当前文件夹下执行`docker-compose pull`拉取最新镜像,然后重新`docker-compose up -d`即可。 + +> 2021.09.21之前部署hoj的请看下面操作 + +### 1、修改MySQL8.0默认的密码加密方式 + +(1)进行hoj-mysql容器 + +```shell +docker exec -it hoj-mysql bash +``` + + (2) 输入对应的mysql密码,进入mysql数据库 + + 注意:-p 后面跟着数据库密码例如hoj123456 + +```shell +mysql -uroot -p数据库密码 +``` + +(3)成功进入后,执行以下命令 + +```shell +mysql> use mysql; + +mysql> grant all PRIVILEGES on *.* to root@'%' WITH GRANT OPTION; + + +mysql> ALTER user 'root'@'%' IDENTIFIED BY '数据库密码' PASSWORD EXPIRE NEVER; + + +mysql> ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '数据库密码'; + + +mysql> FLUSH PRIVILEGES; +``` + +(4) 两次exit 退出mysql和容器 + +### 2、 添加hoj-mysql-checker模块 + +(1)可以选择拉取仓库最新的docker-compose.yml文件(跟部署操作一样,但是会覆盖之前设置的参数)或者访问: + +https://gitee.com/himitzh0730/hoj-deploy/blob/master/standAlone/docker-compose.yml + +(2)或者编辑docker-compose.yml文件,手动添加新模块 + +```yaml + hoj-mysql-checker: + image: registry.cn-shenzhen.aliyuncs.com/hcode/hoj_database_checker + container_name: hoj-mysql-checker + depends_on: + - hoj-mysql + links: + - hoj-mysql:mysql + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-hoj123456} + networks: + hoj-network: + ipv4_address: 172.20.0.8 +``` + +(3) 保存后重启容器即可 + +```shell +docker-compose down + +docker-compose pull + +docker-compose up -d +``` + +**注意**:此次修改成功后,以后更新,都请在对应的docker-compose.yml当前文件夹下执行`docker-compose pull`拉取最新镜像,然后重新`docker-compose up -d`即可。 + +## 二、自定义前端的更新 + +> 附加:如何自定义前端请看这里 => [自定义前端文档](/use/update-fe.html) + +(1)首先到`./hoj/hoj-vue`文件夹中,拉取[hoj-vue](https://gitee.com/himitzh0730/hoj/tree/master/hoj-vue)仓库最新的代码,可能会覆盖本地的修改,请注意合并分支。 + +```shell +git pull +``` + +或者重新直接download成zip包,然后重新自定义修改前端 + +(2)接着,重新用npm打包,在`./hoj/hoj-vue/dist`文件夹会生成静态的前端文件,放到原来指定的位置即可 + +```shell +npm run build +``` + +(3)其它模块的更新,都请在对应的docker-compose.yml当前文件夹下执行`docker-compose pull`拉取最新镜像,然后重新`docker-compose up -d`即可。 \ No newline at end of file diff --git a/docs/docs/deploy/backend.md b/docs/docs/monomer/backend.md similarity index 100% rename from docs/docs/deploy/backend.md rename to docs/docs/monomer/backend.md diff --git a/docs/docs/deploy/frontend.md b/docs/docs/monomer/frontend.md similarity index 100% rename from docs/docs/deploy/frontend.md rename to docs/docs/monomer/frontend.md diff --git a/docs/docs/deploy/judgeserver.md b/docs/docs/monomer/judgeserver.md similarity index 99% rename from docs/docs/deploy/judgeserver.md rename to docs/docs/monomer/judgeserver.md index c001541e..6b19be1c 100644 --- a/docs/docs/deploy/judgeserver.md +++ b/docs/docs/monomer/judgeserver.md @@ -203,6 +203,7 @@ services: - "0.0.0.0:8088:8088" # - "0.0.0.0:5050:5050" # 一般不开放安全沙盒端口 privileged: true # 设置容器的权限为root + shm_size: 512mb # docker默认的共享内存区域太小,设置为512M ``` diff --git a/docs/docs/deploy/mysql-checker.md b/docs/docs/monomer/mysql-checker.md similarity index 100% rename from docs/docs/deploy/mysql-checker.md rename to docs/docs/monomer/mysql-checker.md diff --git a/docs/docs/deploy/mysql.md b/docs/docs/monomer/mysql.md similarity index 100% rename from docs/docs/deploy/mysql.md rename to docs/docs/monomer/mysql.md diff --git a/docs/docs/deploy/nacos.md b/docs/docs/monomer/nacos.md similarity index 100% rename from docs/docs/deploy/nacos.md rename to docs/docs/monomer/nacos.md diff --git a/docs/docs/deploy/redis.md b/docs/docs/monomer/redis.md similarity index 100% rename from docs/docs/deploy/redis.md rename to docs/docs/monomer/redis.md diff --git a/docs/docs/deploy/rsync.md b/docs/docs/monomer/rsync.md similarity index 100% rename from docs/docs/deploy/rsync.md rename to docs/docs/monomer/rsync.md diff --git a/docs/docs/use/close-free-cdn.md b/docs/docs/use/close-free-cdn.md new file mode 100644 index 00000000..575d7cfc --- /dev/null +++ b/docs/docs/use/close-free-cdn.md @@ -0,0 +1,195 @@ +# 取消前端免费CDN + +由于有的机房的网络不支持一些域名的访问,有防火墙挡住,所以可能前端页面的js和css的CDN访问不了,导致页面打不开。 + +hoj挂载了一些前端库的免费CDN,全部都是该域名`cdn.jsdelivr.net`下的免费CDN + +可以在对应的电脑浏览器上打开以下链接,如果能正常访问则没有问题 + +```html +https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js +``` + +如果有问题,有两种办法解决: + +> 前提:hoj前端文件不挂载CDN,最终打包生成的文件夹大小约8MB + +- 如果本身hoj部署在**学校内网机器**上或者**云服务器是无带宽上限、按流量计费的实例**,那么可以不用考虑带宽问题,可以直接取消CDN挂载,直接全部自己打包成对应的静态文件,然后挂载到docker的`hoj-frontend`镜像里面,操作如下: + + 1. 下载前端源代码:[https://gitee.com/himitzh0730/hoj/tree/master/hoj-vue](https://gitee.com/himitzh0730/hoj/tree/master/hoj-vue) + + 2. 进入`hoj-vue`文件夹,编辑`vue.config.js`文件,按下面的修改 + + ```js + // 该变量改成false + const isProduction = false; + + // 本地环境是否需要使用cdn,该变量改成false + const devNeedCdn = false; + + // 找到下面对应的cdn的js链接和css链接,全部注释掉 + css: [ + // 'https://cdn.jsdelivr.net/npm/element-ui@2.14.0/lib/theme-chalk/index.css', + // "https://cdn.jsdelivr.net/npm/github-markdown-css@4.0.0/github-markdown.min.css", + // "https://cdn.jsdelivr.net/npm/vxe-table@2.9.26/lib/style.css", + ], + js: [ + // "https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js", + // "https://cdn.jsdelivr.net/npm/vue-router@3.2.0", + // "https://cdn.jsdelivr.net/npm/axios@0.21.0", + // "https://cdn.jsdelivr.net/npm/vuex@3.5.1", + // "https://cdn.jsdelivr.net/npm/element-ui@2.15.3/lib/index.js", + // "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.3.2/build/highlight.min.js", + // "https://cdn.jsdelivr.net/npm/xe-utils", + // "https://cdn.jsdelivr.net/npm/vxe-table@2.9.26", + // "https://cdn.jsdelivr.net/npm/moment@2.29.1/min/moment.min.js", + // "https://cdn.jsdelivr.net/npm/moment@2.29.1/locale/zh-cn.min.js", + // "https://cdn.jsdelivr.net/npm/moment@2.29.1/locale/en-gb.min.js", + // "https://cdn.jsdelivr.net/npm/echarts@4.9.0/dist/echarts.min.js", + // "https://cdn.jsdelivr.net/npm/vue-echarts@5.0.0-beta.0/dist/vue-echarts.js", + // "https://unpkg.com/mavon-editor@2.9.1/dist/mavon-editor.js" + ] + ``` + + 3. 进入`hoj-vue/src`文件夹,编辑`main.js`文件,将内容替换成如下: + + ```js + import Vue from 'vue' + import App from './App.vue' + import store from './store' + import Element from 'element-ui' + import i18n from '@/i18n' + + import "element-ui/lib/theme-chalk/index.css" + import 'font-awesome/css/font-awesome.min.css' + import Message from 'vue-m-message' + import 'vue-m-message/dist/index.css' + import axios from 'axios' + + import Md_Katex from '@iktakahiro/markdown-it-katex' + + import 'xe-utils' + import VXETable from 'vxe-table' + import 'vxe-table/lib/style.css' + + import Katex from '@/common/katex' + + import VueClipboard from 'vue-clipboard2' + + import highlight from '@/common/highlight' + + import filters from '@/common/filters.js' + import VueCropper from 'vue-cropper' + + import ECharts from 'vue-echarts/components/ECharts.vue' + import 'echarts/lib/chart/bar' + import 'echarts/lib/chart/line' + import 'echarts/lib/chart/pie' + import 'echarts/lib/component/title' + import 'echarts/lib/component/grid' + import 'echarts/lib/component/dataZoom' + import 'echarts/lib/component/legend' + import 'echarts/lib/component/tooltip' + import 'echarts/lib/component/toolbox' + import 'echarts/lib/component/markPoint' + Vue.component('ECharts', ECharts) + + import VueECharts from 'vue-echarts'; + Vue.component('ECharts', VueECharts) + + + import VueParticles from 'vue-particles' + import SlideVerify from 'vue-monoplasty-slide-verify' + + // markdown编辑器 + import mavonEditor from 'mavon-editor' //引入markdown编辑器 + import 'mavon-editor/dist/css/index.css'; + Vue.use(mavonEditor) + + import {Drawer,List,Menu,Icon,AppBar,Button,Divider} from 'muse-ui'; + import 'muse-ui/dist/muse-ui.css'; + + import router from './router' + Vue.use(Drawer) + Vue.use(List) + Vue.use(Menu) + Vue.use(Icon) + Vue.use(AppBar) + Vue.use(Button) + Vue.use(Divider) + + Object.keys(filters).forEach(key => { // 注册全局过滤器 + Vue.filter(key, filters[key]) + }) + Vue.use(VueParticles) // 粒子特效背景 + Vue.use(Katex) // 数学公式渲染 + Vue.use(VXETable) // 表格组件 + Vue.use(VueClipboard) // 剪贴板 + Vue.use(highlight) // 代码高亮 + Vue.use(Element,{ + i18n: (key, value) => i18n.t(key, value) + }) + + Vue.use(VueCropper) // 图像剪切 + Vue.use(Message, { name: 'msg' }) // `Vue.prototype.$msg` 全局消息提示 + + Vue.use(SlideVerify) // 滑动验证码组件 + + Vue.prototype.$axios = axios + + Vue.prototype.$markDown = mavonEditor.markdownIt.use(Md_Katex) // 挂载到vue + + Vue.config.productionTip = false + new Vue({ + router, + store, + i18n, + render: h => h(App) + }).$mount('#app') + + ``` + + 4. 然后使用在`hoj-vue`目录下,使用`npm run build`,npm请自行百度下载安装,之后会生成一个dist文件夹,结构如下: + + ``` + dist + ├── index.html + ├── favicon.ico + └── assets + ├── css + │ ├── .... + ├── fonts + │ ├── .... + ├── img + │ ├── .... + ├── js + │ ├── .... + + .... + .... + ``` + + 将 `dist` 文件夹复制到服务器上某个目录下,比如 `/hoj/www/html/dist`,然后修改 `docker-compose.yml`,在 `hoj-frontend` 模块中的 `volumes` 中增加一行 `- /hoj/www/html/dist:/usr/share/nginx/html` (冒号前面的请修改为实际的路径),然后 `docker-compose up -d` 即可。 + + + +- 如果云服务器是只有固定小流量出口带宽的,例如1M,2M的,害怕访问速度太慢,但是有钱买CDN服务器,可以先按照上面的方式,生成对应的本地静态文件夹,然后把`dist/assets`文件夹放在CDN服务器上,然后修改`dist/index.html` + + **(建议:有弄过CDN的可以这样搞)** + + 添加css等文件的导入 + + ```html + + ``` + + 添加js等文件的导入 + + ```html + + diff --git a/hoj-vue/src/views/admin/training/Training.vue b/hoj-vue/src/views/admin/training/Training.vue new file mode 100644 index 00000000..66af6142 --- /dev/null +++ b/hoj-vue/src/views/admin/training/Training.vue @@ -0,0 +1,250 @@ + + + + diff --git a/hoj-vue/src/views/admin/training/TrainingList.vue b/hoj-vue/src/views/admin/training/TrainingList.vue new file mode 100644 index 00000000..7a5de3b7 --- /dev/null +++ b/hoj-vue/src/views/admin/training/TrainingList.vue @@ -0,0 +1,235 @@ + + + + diff --git a/hoj-vue/src/views/admin/training/TrainingProblemList.vue b/hoj-vue/src/views/admin/training/TrainingProblemList.vue new file mode 100644 index 00000000..f5890ebd --- /dev/null +++ b/hoj-vue/src/views/admin/training/TrainingProblemList.vue @@ -0,0 +1,426 @@ + + + + + diff --git a/hoj-vue/src/views/oj/contest/ContestDetails.vue b/hoj-vue/src/views/oj/contest/ContestDetails.vue index 1aeca2a0..f11b8e5d 100644 --- a/hoj-vue/src/views/oj/contest/ContestDetails.vue +++ b/hoj-vue/src/views/oj/contest/ContestDetails.vue @@ -71,7 +71,11 @@ style="text-align:center" >
- {{ $t('m.Password_Required') }} + + {{ $t('m.Password_Required') }}

{{ $t('m.To_Enter_Need_Password') }} @@ -82,6 +86,7 @@ type="password" :placeholder="$t('m.Enter_the_contest_password')" @keydown.enter.native="checkPassword" + style="width:70%" /> - + @@ -181,45 +181,63 @@ > - {{ problem.displayId }} + + +

+ {{ problem.displayId + '. ' + problem.displayTitle }} +
+ {{ 'Accepted: ' + problem.ac }} +
+ {{ 'Rejected: ' + (problem.total - problem.ac) }} +
+ {{ problem.displayId }} + + + {{ row.submissionInfo[problem.displayId].tryNum }}{{ row.submissionInfo[problem.displayId].tryNum > 1 ? ' tries' : ' try' - }}) + }} @@ -229,6 +247,7 @@ { dataRank[i][problemID] = info[problemID]; - dataRank[i][problemID].ACTime = time.secondFormat( - dataRank[i][problemID].ACTime - ); + if (dataRank[i][problemID].ACTime != null) { + dataRank[i][problemID].errorNum += 1; + dataRank[i][problemID].specificTime = this.parseTimeToSpecific( + dataRank[i][problemID].ACTime + ); + dataRank[i][problemID].ACTime = parseInt( + dataRank[i][problemID].ACTime / 60 + ); + } let status = info[problemID]; if (status.isFirstAC) { cellClass[problemID] = 'first-ac'; @@ -451,7 +480,7 @@ export default { this.options.legend.data = users; this.options.series = seriesData; }, - parseTotalTime(totalTime) { + parseTimeToSpecific(totalTime) { return time.secondFormat(totalTime); }, downloadRankCSV() { @@ -536,4 +565,11 @@ a.emphasis:hover { padding-left: 5px !important; padding-right: 5px !important; } +.submission-time { + font-size: 15.6px; + font-family: Roboto, sans-serif; +} +.submission-error { + font-weight: 400; +} diff --git a/hoj-vue/src/views/oj/contest/children/OIContestRank.vue b/hoj-vue/src/views/oj/contest/children/OIContestRank.vue index 191ea88d..86fbf9f6 100644 --- a/hoj-vue/src/views/oj/contest/children/OIContestRank.vue +++ b/hoj-vue/src/views/oj/contest/children/OIContestRank.vue @@ -290,6 +290,10 @@ export default { mounted() { this.contestID = this.$route.params.contestID; this.getContestRankData(1); + if (!this.refreshDisabled) { + this.autoRefresh = true; + this.handleAutoRefresh(true); + } }, computed: { contest() { diff --git a/hoj-vue/src/views/oj/problem/Problem.vue b/hoj-vue/src/views/oj/problem/Problem.vue index 9671dcc4..b870be69 100644 --- a/hoj-vue/src/views/oj/problem/Problem.vue +++ b/hoj-vue/src/views/oj/problem/Problem.vue @@ -634,6 +634,7 @@ import { CONTEST_STATUS, JUDGE_STATUS_RESERVE, buildProblemCodeKey, + buildIndividualLanguageAndThemeKey, RULE_TYPE, PROBLEM_LEVEL, } from '@/common/constants'; @@ -718,6 +719,15 @@ export default { vm.theme = problemCode.theme; }); } else { + let individualLanguageAndTheme = storage.get( + buildIndividualLanguageAndThemeKey() + ); + if (individualLanguageAndTheme) { + next((vm) => { + vm.language = individualLanguageAndTheme.language; + vm.theme = individualLanguageAndTheme.theme; + }); + } next(); } }, @@ -966,7 +976,14 @@ export default { return; } // try to load problem template - this.language = this.problemData.languages[0]; + if (this.problemData.languages.length != 0) { + if ( + !this.language || + this.problemData.languages.indexOf(this.language) == -1 + ) { + this.language = this.problemData.languages[0]; + } + } let codeTemplate = this.problemData.codeTemplate; if (codeTemplate && codeTemplate[this.language]) { this.code = codeTemplate[this.language]; @@ -1320,6 +1337,12 @@ export default { language: this.language, theme: this.theme, }); + + storage.set(buildIndividualLanguageAndThemeKey(), { + language: this.language, + theme: this.theme, + }); + next(); }, watch: { diff --git a/hoj-vue/src/views/oj/status/SubmissionList.vue b/hoj-vue/src/views/oj/status/SubmissionList.vue index 3ecfd8f7..a0417ba4 100644 --- a/hoj-vue/src/views/oj/status/SubmissionList.vue +++ b/hoj-vue/src/views/oj/status/SubmissionList.vue @@ -545,6 +545,9 @@ export default { let viewData = this.$refs.xTable.getTableData().tableData; for (let key in submitIds) { let submitId = parseInt(key); + if (!result[submitId]) { + continue; + } // 更新数据列表 this.submissions[submitIds[key]] = result[submitId]; // 更新view中的结果,f分数,耗时,空间消耗,判题机ip @@ -564,7 +567,7 @@ export default { delete this.needCheckSubmitIds[key]; } } - // 当前提交列表的提交都判题结束或者检查结果180s(2s*90)还没判题结束,为了避免无用请求加重服务器负担,直接停止检查的请求。 + // 当前提交列表的提交都判题结束或者检查结果600s(2s*300)还没判题结束,为了避免无用请求加重服务器负担,直接停止检查的请求。 if ( Object.keys(this.needCheckSubmitIds).length == 0 || this.checkStatusNum == 300 diff --git a/hoj-vue/src/views/oj/training/TrainingDetails.vue b/hoj-vue/src/views/oj/training/TrainingDetails.vue new file mode 100644 index 00000000..eacc9a10 --- /dev/null +++ b/hoj-vue/src/views/oj/training/TrainingDetails.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/hoj-vue/src/views/oj/training/TrainingList.vue b/hoj-vue/src/views/oj/training/TrainingList.vue new file mode 100644 index 00000000..2bb819c2 --- /dev/null +++ b/hoj-vue/src/views/oj/training/TrainingList.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/hoj-vue/src/views/oj/training/TrainingProblemList.vue b/hoj-vue/src/views/oj/training/TrainingProblemList.vue new file mode 100644 index 00000000..de3ad02e --- /dev/null +++ b/hoj-vue/src/views/oj/training/TrainingProblemList.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/hoj-vue/src/views/oj/training/TrainingRank.vue b/hoj-vue/src/views/oj/training/TrainingRank.vue new file mode 100644 index 00000000..611d3ae5 --- /dev/null +++ b/hoj-vue/src/views/oj/training/TrainingRank.vue @@ -0,0 +1,484 @@ + + + diff --git a/hoj-vue/vue.config.js b/hoj-vue/vue.config.js index 42950c8c..2912fba4 100644 --- a/hoj-vue/vue.config.js +++ b/hoj-vue/vue.config.js @@ -5,7 +5,7 @@ const CompressionWebpackPlugin = require('compression-webpack-plugin'); // 开 const isProduction = process.env.NODE_ENV === 'production'; // 本地环境是否需要使用cdn -const devNeedCdn = true +const devNeedCdn = true; // cdn链接 const cdn = { diff --git a/sqlAndsetting/hoj-update.sql b/sqlAndsetting/hoj-update.sql index 50366ee8..ad109f1a 100644 --- a/sqlAndsetting/hoj-update.sql +++ b/sqlAndsetting/hoj-update.sql @@ -378,20 +378,22 @@ IF NOT EXISTS ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `training_problem` ( - `id` bigint unsigned NOT NULL, + `id` bigint unsigned NOT NULL AUTO_INCREMENT, `tid` bigint unsigned NOT NULL COMMENT '训练id', `pid` bigint unsigned NOT NULL COMMENT '题目id', - `rank` int DEFAULT '0' COMMENT '排序用', + `rank` int DEFAULT '0', + `display_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `tid` (`tid`), KEY `pid` (`pid`), + KEY `display_id` (`display_id`), CONSTRAINT `training_problem_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `training` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `training_problem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `training_problem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_problem_ibfk_3` FOREIGN KEY (`display_id`) REFERENCES `problem` (`problem_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - CREATE TABLE `training_record` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `tid` bigint unsigned NOT NULL, @@ -454,3 +456,71 @@ CALL Add_training_table; DROP PROCEDURE Add_training_table; +/* +* 2021.12.05 contest增加auto_real_rank比赛结束是否自动解除封榜,自动转换成真实榜单 + +*/ +DROP PROCEDURE +IF EXISTS contest_Add_auto_real_rank; +DELIMITER $$ + +CREATE PROCEDURE contest_Add_auto_real_rank; () +BEGIN + +IF NOT EXISTS ( + SELECT + 1 + FROM + information_schema.`COLUMNS` + WHERE + table_name = 'conetst' + AND column_name = 'auto_real_rank' +) THEN + ALTER TABLE `hoj`.`contest` ADD COLUMN `auto_real_rank` BOOLEAN DEFAULT 1 NULL COMMENT '比赛结束是否自动解除封榜,自动转换成真实榜单'; + DROP TABLE `hoj`.`training_problem`; + DROP TABLE `hoj`.`training_record`; + CREATE TABLE `training_problem` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `tid` bigint unsigned NOT NULL COMMENT '训练id', + `pid` bigint unsigned NOT NULL COMMENT '题目id', + `rank` int DEFAULT '0', + `display_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `tid` (`tid`), + KEY `pid` (`pid`), + KEY `display_id` (`display_id`), + CONSTRAINT `training_problem_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `training` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_problem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_problem_ibfk_3` FOREIGN KEY (`display_id`) REFERENCES `problem` (`problem_id`) ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + CREATE TABLE `training_record` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `tid` bigint unsigned NOT NULL, + `tpid` bigint unsigned NOT NULL, + `pid` bigint unsigned NOT NULL, + `uid` varchar(255) NOT NULL, + `submit_id` bigint unsigned NOT NULL, + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `tid` (`tid`), + KEY `tpid` (`tpid`), + KEY `pid` (`pid`), + KEY `uid` (`uid`), + KEY `submit_id` (`submit_id`), + CONSTRAINT `training_record_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `training` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_record_ibfk_2` FOREIGN KEY (`tpid`) REFERENCES `training_problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_record_ibfk_3` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_record_ibfk_4` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_record_ibfk_5` FOREIGN KEY (`submit_id`) REFERENCES `judge` (`submit_id`) ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +END +IF ; END$$ + +DELIMITER ; +CALL contest_Add_auto_real_rank; ; + +DROP PROCEDURE contest_Add_auto_real_rank;; diff --git a/sqlAndsetting/hoj.sql b/sqlAndsetting/hoj.sql index 94976c00..0ee75603 100644 --- a/sqlAndsetting/hoj.sql +++ b/sqlAndsetting/hoj.sql @@ -142,6 +142,7 @@ CREATE TABLE `contest` ( `duration` bigint(20) DEFAULT NULL COMMENT '比赛时长(s)', `seal_rank` tinyint(1) DEFAULT '0' COMMENT '是否开启封榜', `seal_rank_time` datetime DEFAULT NULL COMMENT '封榜起始时间,一直到比赛结束,不刷新榜单', + `auto_real_rank` tinyint(1) DEFAULT '1' COMMENT '比赛结束是否自动解除封榜,自动转换成真实榜单', `status` int(11) DEFAULT NULL COMMENT '-1为未开始,0为进行中,1为已结束', `visible` tinyint(1) DEFAULT '1' COMMENT '是否可见', `open_print` tinyint(1) DEFAULT '0' COMMENT '是否打开打印功能', @@ -885,17 +886,20 @@ CREATE TABLE `training_category` ( DROP TABLE IF EXISTS `training_problem`; CREATE TABLE `training_problem` ( - `id` bigint unsigned NOT NULL, + `id` bigint unsigned NOT NULL AUTO_INCREMENT, `tid` bigint unsigned NOT NULL COMMENT '训练id', `pid` bigint unsigned NOT NULL COMMENT '题目id', - `rank` int DEFAULT '0' COMMENT '排序用', + `rank` int DEFAULT '0', + `display_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `tid` (`tid`), KEY `pid` (`pid`), + KEY `display_id` (`display_id`), CONSTRAINT `training_problem_ibfk_1` FOREIGN KEY (`tid`) REFERENCES `training` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `training_problem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `training_problem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `training_problem_ibfk_3` FOREIGN KEY (`display_id`) REFERENCES `problem` (`problem_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `training_record` */