修改前端编辑器样式以及md格式转换
This commit is contained in:
parent
0b3ced6193
commit
702e450df9
|
@ -7,8 +7,10 @@
|
|||
> 当前任务
|
||||
|
||||
- [x] 测试HDU判题整套流程
|
||||
- [ ] 修改前端编辑器样式以及md格式转换
|
||||
- [x] 修改前端编辑器样式以及md格式转换
|
||||
- [ ] 修复代码编辑器bug
|
||||
- [ ] 测试比赛相关接口,验证权限及数据计算
|
||||
- [ ] ?增加codeforce的vj判题
|
||||
- [ ] 部署判题服务器到云服务器,半正式上线HOJ
|
||||
- [ ] 完善文档
|
||||
|
||||
|
@ -40,6 +42,7 @@
|
|||
| 2021-02-16 | 完善测试特殊判题题目的操作 | Himit_ZH |
|
||||
| 2021-02-19 | 正式完善HDU虚拟判题以及题目添加 | Himit_ZH |
|
||||
| 2021-02-20 | 测试HDU判题整套流程完成 | Himit_ZH |
|
||||
| 2021-02-22 | 修改前端编辑器样式以及md格式转换 | Himit_ZH |
|
||||
|
||||
# 二、系统架构
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import top.hcode.hoj.pojo.entity.CompileSpj;
|
|||
import top.hcode.hoj.pojo.entity.Judge;
|
||||
import top.hcode.hoj.pojo.entity.ToJudge;
|
||||
import top.hcode.hoj.service.ToJudgeService;
|
||||
import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.RedisUtils;
|
||||
|
||||
|
@ -36,11 +37,19 @@ public class CloudHandler implements ToJudgeService {
|
|||
@Autowired
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
|
||||
// 调度判题服务器失败,可能是判题服务器有故障,或者全部达到判题最大数,那么将该提交重新进入等待队列
|
||||
@Override
|
||||
public CommonResult submitProblemJudge(ToJudge toJudge) {
|
||||
|
||||
if (toJudge.getTryAgainNum() == 40) {
|
||||
Judge judge = toJudge.getJudge();
|
||||
judge.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus());
|
||||
judge.setErrorMessage("Failed to connect the judgeServer. Please resubmit this submission again!");
|
||||
judgeService.updateById(judge);
|
||||
} else {
|
||||
// 线程沉睡1秒,再将任务重新发布,避免过快问题,同时判题服务过多,导致的失败
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
|
@ -48,7 +57,9 @@ public class CloudHandler implements ToJudgeService {
|
|||
e.printStackTrace();
|
||||
}
|
||||
Judge judge = toJudge.getJudge();
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), toJudge.getToken(), judge.getCid() == 0);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), toJudge.getToken(),
|
||||
judge.getCid() == 0, toJudge.getTryAgainNum() + 1);
|
||||
}
|
||||
return CommonResult.errorResponse("判题服务器繁忙或出错,提交进入重判队列,请等待管理员处理!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
|
@ -66,16 +77,24 @@ public class CloudHandler implements ToJudgeService {
|
|||
account.set("password", toJudge.getPassword());
|
||||
redisUtils.llPush(Constants.Judge.getListNameByOJName(toJudge.getRemoteJudge().split("-")[0]), JSONUtil.toJsonStr(account));
|
||||
|
||||
if (toJudge.getTryAgainNum() == 40) {
|
||||
Judge judge = toJudge.getJudge();
|
||||
judge.setStatus(Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus());
|
||||
judge.setErrorMessage("Failed to connect the judgeServer. Please resubmit this submission again!");
|
||||
judgeService.updateById(judge);
|
||||
} else {
|
||||
|
||||
// 线程沉睡一秒,再将任务重新发布,避免过快问题,同时判题服务过多,导致的失败
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Judge judge = toJudge.getJudge();
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), toJudge.getToken(),
|
||||
toJudge.getRemoteJudge(), judge.getCid() == 0);
|
||||
toJudge.getRemoteJudge(), judge.getCid() == 0, toJudge.getTryAgainNum() + 1);
|
||||
}
|
||||
|
||||
return CommonResult.errorResponse("判题服务器繁忙或出错,提交进入重判队列,请等待管理员处理!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ public class CorsConfig implements WebMvcConfigurer {
|
|||
// 前端直接通过/public/img/图片名称即可拿到
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler(Constants.File.USER_AVATAR_API.getPath()+"**") // /public/img/**
|
||||
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath());
|
||||
registry.addResourceHandler(Constants.File.IMG_API.getPath()+"**") // /public/img/**
|
||||
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath(),
|
||||
"file:" + Constants.File.MARKDOWN_IMG_FOLDER.getPath());
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package top.hcode.hoj.config;
|
||||
|
||||
import com.netflix.loadbalancer.IRule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import top.hcode.hoj.judge.self.JudgeChooseRule;
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ import top.hcode.hoj.judge.self.JudgeChooseRule;
|
|||
* @Date: 2021/2/4 23:10
|
||||
* @Description:
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class RibbonConfig {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -101,9 +101,10 @@ public class AdminJudgeController {
|
|||
// 调用判题服务
|
||||
Problem problem = problemService.getById(judge.getPid());
|
||||
if (problem.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(), judge.getCid() == 0);
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
}
|
||||
return CommonResult.successResponse(judge, "重判成功!该提交已进入判题队列!");
|
||||
} else {
|
||||
|
@ -148,12 +149,13 @@ public class AdminJudgeController {
|
|||
if (problem.getIsRemote()) { // 如果是远程oj判题
|
||||
for (Judge judge : rejudgeList) {
|
||||
// 进入重判队列,等待调用判题服务
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(), judge.getCid() == 0);
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
}
|
||||
} else {
|
||||
for (Judge judge : rejudgeList) {
|
||||
// 进入重判队列,等待调用判题服务
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ public class FileController {
|
|||
|
||||
//更新user_info里面的avatar
|
||||
UpdateWrapper<UserInfo> userInfoUpdateWrapper = new UpdateWrapper<>();
|
||||
userInfoUpdateWrapper.set("avatar", Constants.File.USER_FILE_HOST.getPath() + Constants.File.USER_AVATAR_API.getPath() + filename)
|
||||
userInfoUpdateWrapper.set("avatar", Constants.File.USER_FILE_HOST.getPath() + Constants.File.IMG_API.getPath() + filename)
|
||||
.eq("uuid", userRolesVo.getUid());
|
||||
userInfoService.update(userInfoUpdateWrapper);
|
||||
|
||||
|
@ -158,7 +158,7 @@ public class FileController {
|
|||
.put("uid", userRolesVo.getUid())
|
||||
.put("username", userRolesVo.getUsername())
|
||||
.put("nickname", userRolesVo.getNickname())
|
||||
.put("avatar", Constants.File.USER_FILE_HOST.getPath() + Constants.File.USER_AVATAR_API.getPath() + filename)
|
||||
.put("avatar", Constants.File.USER_FILE_HOST.getPath() + Constants.File.IMG_API.getPath() + filename)
|
||||
.put("email", userRolesVo.getEmail())
|
||||
.put("number", userRolesVo.getNumber())
|
||||
.put("school", userRolesVo.getSchool())
|
||||
|
@ -515,4 +515,60 @@ public class FileController {
|
|||
return "txt";
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/upload-md-img", method = RequestMethod.POST)
|
||||
@RequiresAuthentication
|
||||
@ResponseBody
|
||||
@Transactional
|
||||
@RequiresRoles(value = {"root", "admin"}, logical = Logical.OR)
|
||||
public CommonResult uploadMDImg(@RequestParam("image") MultipartFile image) {
|
||||
if (image == null) {
|
||||
return CommonResult.errorResponse("图片为空!");
|
||||
}
|
||||
if (image.getSize() > 1024 * 1024 * 4) {
|
||||
return CommonResult.errorResponse("上传的图片文件大小不能大于4M!");
|
||||
}
|
||||
//获取文件后缀
|
||||
String suffix = image.getOriginalFilename().substring(image.getOriginalFilename().lastIndexOf(".") + 1);
|
||||
if (!"jpg,jpeg,gif,png,webp".toUpperCase().contains(suffix.toUpperCase())) {
|
||||
return CommonResult.errorResponse("请选择jpg,jpeg,gif,png,webp格式的图片!");
|
||||
}
|
||||
File savePathFile = new File(Constants.File.MARKDOWN_IMG_FOLDER.getPath());
|
||||
if (!savePathFile.exists()) {
|
||||
//若不存在该目录,则创建目录
|
||||
savePathFile.mkdir();
|
||||
}
|
||||
//通过UUID生成唯一文件名
|
||||
String filename = IdUtil.simpleUUID() + "." + suffix;
|
||||
try {
|
||||
//将文件保存指定目录
|
||||
image.transferTo(new File(Constants.File.MARKDOWN_IMG_FOLDER.getPath() + filename));
|
||||
} catch (Exception e) {
|
||||
log.error("图片文件上传异常-------------->{}", e.getMessage());
|
||||
return CommonResult.errorResponse("服务器异常:图片文件上传失败!", CommonResult.STATUS_ERROR);
|
||||
}
|
||||
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("link", Constants.File.USER_FILE_HOST.getPath() + Constants.File.IMG_API.getPath() + filename)
|
||||
.put("filePath",Constants.File.MARKDOWN_IMG_FOLDER.getPath() + filename).map(),
|
||||
"上传图片成功!");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/delete-md-img", method = RequestMethod.GET)
|
||||
@RequiresAuthentication
|
||||
@ResponseBody
|
||||
@Transactional
|
||||
@RequiresRoles(value = {"root", "admin"}, logical = Logical.OR)
|
||||
public CommonResult uploadMDImg(@RequestParam("filePath") String filePath) {
|
||||
boolean result = FileUtil.del(filePath);
|
||||
if (result){
|
||||
return CommonResult.successResponse(null,"删除成功");
|
||||
}else{
|
||||
return CommonResult.errorResponse("删除失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -211,9 +211,10 @@ public class JudgeController {
|
|||
|
||||
// 将提交加入任务队列
|
||||
if (judgeDto.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judgeDto.getPid(), judge.getCid() == 0);
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judgeDto.getPid(),
|
||||
judge.getCid() == 0, 1);
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0);
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, judge.getCid() == 0, 1);
|
||||
}
|
||||
|
||||
return CommonResult.successResponse(judge, "数据提交成功!");
|
||||
|
@ -223,14 +224,14 @@ public class JudgeController {
|
|||
/**
|
||||
* @MethodName resubmit
|
||||
* @Params * @param null
|
||||
* @Description 远程虚拟判题的提交失败后,用户点击按钮重新提交判题进入的方法
|
||||
* @Description 调用判题服务器提交失败超过60s后,用户点击按钮重新提交判题进入的方法
|
||||
* @Return
|
||||
* @Since 2021/2/12
|
||||
*/
|
||||
@RequiresAuthentication
|
||||
@GetMapping(value = "/resubmit")
|
||||
public CommonResult resubmit(@RequestParam("submitId")Long submitId,
|
||||
HttpServletRequest request){
|
||||
public CommonResult resubmit(@RequestParam("submitId") Long submitId,
|
||||
HttpServletRequest request) {
|
||||
|
||||
Judge judge = judgeService.getById(submitId);
|
||||
if (judge == null) {
|
||||
|
@ -239,7 +240,7 @@ public class JudgeController {
|
|||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
if (!judge.getUid().equals(userRolesVo.getUid())){ // 不是本人无法重新提交
|
||||
if (!judge.getUid().equals(userRolesVo.getUid())) { // 不是本人无法重新提交
|
||||
return CommonResult.errorResponse("对不起!您并非提交数据的本人,无法重新提交!");
|
||||
}
|
||||
Problem problem = problemService.getById(judge.getPid());
|
||||
|
@ -247,8 +248,14 @@ public class JudgeController {
|
|||
judge.setStatus(Constants.Judge.STATUS_PENDING.getStatus());
|
||||
judge.setErrorMessage(null);
|
||||
judgeService.updateById(judge);
|
||||
// 调用判题服务
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), problem.getId(), judgeToken, problem.getSource(), judge.getCid() == 0);
|
||||
// 将提交加入任务队列
|
||||
if (problem.getIsRemote()) { // 如果是远程oj判题
|
||||
remoteJudgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken, problem.getProblemId(),
|
||||
judge.getCid() == 0, 1);
|
||||
} else {
|
||||
judgeDispatcher.sendTask(judge.getSubmitId(), judge.getPid(), judgeToken,
|
||||
judge.getCid() == 0, 1);
|
||||
}
|
||||
return CommonResult.successResponse(judge, "重新提交成功!");
|
||||
}
|
||||
|
||||
|
@ -291,9 +298,10 @@ public class JudgeController {
|
|||
Problem problem = problemService.getById(judge.getPid());
|
||||
HashMap<String, Object> result = new HashMap<>();
|
||||
|
||||
// 只允许用户查看ce错误和se错误信息提示
|
||||
// 只允许用户查看ce错误,sf错误,se错误信息提示
|
||||
if (judge.getStatus().intValue() != Constants.Judge.STATUS_COMPILE_ERROR.getStatus() &&
|
||||
judge.getStatus().intValue() != Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()) {
|
||||
judge.getStatus().intValue() != Constants.Judge.STATUS_SYSTEM_ERROR.getStatus() &&
|
||||
judge.getStatus().intValue() != Constants.Judge.STATUS_SUBMITTED_FAILED.getStatus()) {
|
||||
judge.setErrorMessage("");
|
||||
}
|
||||
result.put("submission", judge);
|
||||
|
|
|
@ -36,7 +36,7 @@ public class HDUProblemStrategy extends ProblemStrategy {
|
|||
info.setTitle(ReUtil.get("color:#1A5CC8\">([\\s\\S]*?)</h1>", html, 1).trim());
|
||||
info.setTimeLimit(Integer.parseInt(ReUtil.get("(\\d*) MS", html, 1)));
|
||||
info.setMemoryLimit(Integer.parseInt(ReUtil.get("/(\\d*) K", html, 1)) / 1024);
|
||||
info.setDescription(ReUtil.get(">Problem Description</div>\\s+<.*?>(.*?)<br></div>", html, 1));
|
||||
info.setDescription(ReUtil.get(">Problem Description</div>\\s+<.*?>(.*?)<br></div>", html, 1).replaceAll("src=\"../../", "src=\"" + HOST + "/"));
|
||||
info.setInput(ReUtil.get(">Input</div>.*?<.*?>(.*?)<br></div>", html, 1));
|
||||
info.setOutput(ReUtil.get(">Output</div>.*?<.*?>(.*?)<br></div>", html, 1));
|
||||
StringBuilder sb = new StringBuilder("<input>");
|
||||
|
|
|
@ -21,13 +21,14 @@ public class RemoteJudgeDispatcher {
|
|||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
public void sendTask(Long submitId, Long pid, String token, String remoteJudge, Boolean isContest) {
|
||||
public void sendTask(Long submitId, Long pid, String token, String remoteJudge, Boolean isContest, Integer tryAgainNum) {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
task.set("pid", pid);
|
||||
task.set("remoteJudge", remoteJudge);
|
||||
task.set("token", token);
|
||||
task.set("isContest", isContest);
|
||||
task.set("tryAgainNum", tryAgainNum);
|
||||
try {
|
||||
String account = (String) redisUtils.lrPop(Constants.Judge.getListNameByOJName(remoteJudge.split("-")[0]));
|
||||
if (account != null) {
|
||||
|
|
|
@ -43,8 +43,9 @@ public class RemoteJudgeReceiver implements MessageListener {
|
|||
Boolean isContest = task.getBool("isContest");
|
||||
String username = task.getStr("username");
|
||||
String password = task.getStr("password");
|
||||
Integer tryAgainNum = task.getInt("tryAgainNum");
|
||||
if (username == null || password == null) {
|
||||
remoteJudgeDispatcher.sendTask(submitId, pid, token, remoteJudge, isContest);
|
||||
remoteJudgeDispatcher.sendTask(submitId, pid, token, remoteJudge, isContest, tryAgainNum);
|
||||
return;
|
||||
}
|
||||
Judge judge = judgeService.getById(submitId);
|
||||
|
@ -54,6 +55,7 @@ public class RemoteJudgeReceiver implements MessageListener {
|
|||
.setToken(token)
|
||||
.setRemoteJudge(remoteJudge)
|
||||
.setUsername(username)
|
||||
.setPassword(password));
|
||||
.setPassword(password)
|
||||
.setTryAgainNum(tryAgainNum));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,15 +38,13 @@ public class JudgeChooseRule extends AbstractLoadBalancerRule {
|
|||
|
||||
@Override
|
||||
public Server choose(Object key) {
|
||||
// 获取配置文件中所配置的集群名称
|
||||
String clusterName = discoveryProperties.getClusterName();
|
||||
|
||||
// // 获取配置文件中所配置的集群名称
|
||||
// String clusterName = discoveryProperties.getClusterName();
|
||||
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
|
||||
// 需要请求的微服务名称
|
||||
String serviceId = loadBalancer.getName();
|
||||
// 获取该微服务的所有健康实例
|
||||
List<Instance> instances = getInstances(serviceId);
|
||||
System.out.println(instances);
|
||||
// 进行匹配筛选的实例列表
|
||||
List<Instance> metadataMatchInstances;
|
||||
// 过滤出小于或等于规定最大并发判题任务数的服务实例
|
||||
|
|
|
@ -25,12 +25,13 @@ public class JudgeDispatcher {
|
|||
@Autowired
|
||||
private JudgeServiceImpl judgeService;
|
||||
|
||||
public void sendTask(Long submitId, Long pid, String token, Boolean isContest) {
|
||||
public void sendTask(Long submitId, Long pid, String token, Boolean isContest,Integer tryAgainNum) {
|
||||
JSONObject task = new JSONObject();
|
||||
task.set("submitId", submitId);
|
||||
task.set("pid", pid);
|
||||
task.set("token", token);
|
||||
task.set("isContest", isContest);
|
||||
task.set("tryAgainNum", tryAgainNum);
|
||||
|
||||
try {
|
||||
redisUtils.sendMessage(Constants.Judge.STATUS_JUDGE_WAITING.getName(), JSONUtil.toJsonStr(task));
|
||||
|
|
|
@ -18,8 +18,7 @@ import top.hcode.hoj.service.impl.JudgeServiceImpl;
|
|||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/2/5 16:43
|
||||
* @Description:
|
||||
* 1. 判题信息的接受者,调用判题服务,对提交代码进行判断,
|
||||
* @Description: 1. 判题信息的接受者,调用判题服务,对提交代码进行判断,
|
||||
* 2. 若无空闲判题服务器,会自动进入熔断机制,重新将该判题信息发布到频道内,
|
||||
* 3. 再次接受到信息,再次查询是否有空闲判题服务器,若有则进行判题,否则回到2
|
||||
*/
|
||||
|
@ -41,16 +40,18 @@ public class JudgeReceiver implements MessageListener {
|
|||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
// StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
// //接收的topic
|
||||
// String channel = stringRedisSerializer.deserialize(message.getChannel());
|
||||
// 进行反序列化获取信息
|
||||
String taskJson = (String)jackson2JsonRedisSerializer.deserialize(message.getBody());
|
||||
|
||||
String taskJson = (String) jackson2JsonRedisSerializer.deserialize(message.getBody());
|
||||
JSONObject task = JSONUtil.parseObj(taskJson);
|
||||
Long submitId = task.getLong("submitId");
|
||||
String token = task.getStr("token");
|
||||
Integer tryAgainNum = task.getInt("tryAgainNum");
|
||||
Judge judge = judgeService.getById(submitId);
|
||||
// 调用判题服务
|
||||
toJudgeService.submitProblemJudge(new ToJudge().setJudge(judge).setToken(token).setRemoteJudge(null));
|
||||
toJudgeService.submitProblemJudge(new ToJudge()
|
||||
.setJudge(judge)
|
||||
.setToken(token)
|
||||
.setRemoteJudge(null)
|
||||
.setTryAgainNum(tryAgainNum));
|
||||
}
|
||||
}
|
|
@ -223,7 +223,7 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
|
|||
checkProblemCase = remove && add;
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if (problemDto.getSamples().size() > 0) {
|
||||
int sumScore = 0;
|
||||
// 新增加的case列表
|
||||
List<ProblemCase> newProblemCaseList = new LinkedList<>();
|
||||
|
|
|
@ -25,11 +25,11 @@ public class Constants {
|
|||
STATUS_JUDGING(7, "Judging", null),
|
||||
STATUS_PARTIAL_ACCEPTED(8, "Partial Accepted", "pa"),
|
||||
STATUS_SUBMITTING(9, "Submitting", null),
|
||||
STATUS_SUBMITTED_FAILED(10,"Submitted Failed",null),
|
||||
STATUS_SUBMITTED_FAILED(10, "Submitted Failed", null),
|
||||
STATUS_NULL(15, "No Status", null),
|
||||
STATUS_JUDGE_WAITING(-100, "Waiting Queue", null),
|
||||
STATUS_REMOTE_JUDGE_WAITING_HANDLE(-200, "Remote Waiting Handle Queue", null),
|
||||
STATUS_HDU_REMOTE_JUDGE_ACCOUNT(-500,"Hdu Remote Judge Account",null);
|
||||
STATUS_HDU_REMOTE_JUDGE_ACCOUNT(-500, "Hdu Remote Judge Account", null);
|
||||
|
||||
private Judge(Integer status, String name, String columnName) {
|
||||
this.status = status;
|
||||
|
@ -141,8 +141,12 @@ public class Constants {
|
|||
public enum File {
|
||||
|
||||
USER_FILE_HOST("http://localhost:9010"),
|
||||
|
||||
USER_AVATAR_FOLDER("D:\\avatar\\"),
|
||||
USER_AVATAR_API("/public/img/"),
|
||||
|
||||
MARKDOWN_IMG_FOLDER("D:\\md\\"),
|
||||
|
||||
IMG_API("/public/img/"),
|
||||
|
||||
TESTCASE_BASE_FOLDER("D:\\zip\\"),
|
||||
|
||||
|
|
|
@ -8,12 +8,6 @@ service-url:
|
|||
hoj-judge-server: http://hoj-judge-server # 服务访问base_url
|
||||
name: hoj-judge-server # 服务名
|
||||
|
||||
|
||||
#激活Sentinel对Feign的支持
|
||||
feign:
|
||||
sentinel:
|
||||
enabled: true
|
||||
|
||||
spring:
|
||||
profiles: dev
|
||||
# 配置文件上传限制
|
||||
|
@ -79,9 +73,9 @@ shiro-redis:
|
|||
|
||||
ribbon:
|
||||
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,俩端连接所用的时间 单位是秒
|
||||
ReadTimeout: 6000
|
||||
ReadTimeout: 10000
|
||||
# 指的是建立连接后从服务器读取到可用资源的时间
|
||||
ConnectTimeout: 5000
|
||||
ConnectTimeout: 30000
|
||||
|
||||
logging:
|
||||
level:
|
||||
|
@ -90,3 +84,20 @@ logging:
|
|||
nacos:
|
||||
client:
|
||||
naming: error
|
||||
|
||||
# 开启Hystrix断路器
|
||||
feign:
|
||||
hystrix:
|
||||
enabled: true
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
connectTimeout: 15000
|
||||
readTimeout: 60000
|
||||
hystrix:
|
||||
command:
|
||||
default:
|
||||
execution:
|
||||
isolation:
|
||||
thread:
|
||||
timeoutInMilliseconds: 60000
|
||||
|
|
|
@ -8,12 +8,6 @@ service-url:
|
|||
hoj-judge-server: http://hoj-judge-server # 服务访问base_url
|
||||
name: hoj-judge-server # 服务名
|
||||
|
||||
|
||||
#激活Sentinel对Feign的支持
|
||||
feign:
|
||||
sentinel:
|
||||
enabled: true
|
||||
|
||||
spring:
|
||||
profiles: dev
|
||||
# 配置文件上传限制
|
||||
|
@ -79,9 +73,9 @@ shiro-redis:
|
|||
|
||||
ribbon:
|
||||
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,俩端连接所用的时间 单位是秒
|
||||
ReadTimeout: 6000
|
||||
ReadTimeout: 10000
|
||||
# 指的是建立连接后从服务器读取到可用资源的时间
|
||||
ConnectTimeout: 5000
|
||||
ConnectTimeout: 30000
|
||||
|
||||
logging:
|
||||
level:
|
||||
|
@ -90,3 +84,20 @@ logging:
|
|||
nacos:
|
||||
client:
|
||||
naming: error
|
||||
|
||||
# 开启Hystrix断路器
|
||||
feign:
|
||||
hystrix:
|
||||
enabled: true
|
||||
client:
|
||||
config:
|
||||
default:
|
||||
connectTimeout: 15000
|
||||
readTimeout: 60000
|
||||
hystrix:
|
||||
command:
|
||||
default:
|
||||
execution:
|
||||
isolation:
|
||||
thread:
|
||||
timeoutInMilliseconds: 60000
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -67,7 +67,7 @@ public class RemoteJudgeResultReceiver implements MessageListener {
|
|||
// TODO 如果结果没出来,重新放入队列并更新状态为Waiting
|
||||
if (status.equals(Constants.Judge.STATUS_PENDING.getStatus())) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
remoteJudgeResultDispatcher.sendTask(remoteJudge, submitId, uid, cid, pid, resultSubmitId);
|
||||
} catch (Exception e) {
|
||||
log.error("重新查询结果任务出错------{}", e.getMessage());
|
||||
|
|
Binary file not shown.
|
@ -36,7 +36,7 @@ public class ToJudge implements Serializable {
|
|||
@ApiModelProperty("远程判题所用密码")
|
||||
private String password;
|
||||
|
||||
// @ApiModelProperty("重新尝试的次数,三次重新调用判题机依旧失败,直接判为系统错误!")
|
||||
// private Integer tryAgainNum;
|
||||
@ApiModelProperty("重新尝试的次数,40次重新调用判题机依旧失败,直接判为提交失败!")
|
||||
private Integer tryAgainNum;
|
||||
|
||||
}
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -17,12 +17,11 @@
|
|||
"highlight.js": "^10.3.2",
|
||||
"jquery": "^3.5.1",
|
||||
"katex": "^0.12.0",
|
||||
"mavon-editor": "^2.9.1",
|
||||
"moment": "^2.29.1",
|
||||
"muse-ui": "^3.0.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"papaparse": "^5.3.0",
|
||||
"tar-simditor": "^3.0.5",
|
||||
"tar-simditor-markdown": "^1.2.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-avatar": "^2.3.3",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
|
|
|
@ -80,6 +80,7 @@ export default {
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
touch-action: none;
|
||||
}
|
||||
body {
|
||||
background-color: #eee !important;
|
||||
|
@ -264,4 +265,7 @@ a:hover {
|
|||
.el-tag--dark {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
.v-note-wrapper .v-note-panel {
|
||||
height: 460px !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
/* Fira Code */
|
||||
@font-face {
|
||||
font-family: 'Fira Code', UbuntuMono, 'PingFang SC', 'Microsoft YaHei',
|
||||
Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
|
||||
font-style: normal;
|
||||
src: local('Fira Code'), url('maize/FiraCode-Regular.ttf') format('ttf');
|
||||
}
|
||||
|
||||
/* 全局属性 */
|
||||
:root {
|
||||
--select-text-bg-color: #e49123;
|
||||
--bg-color: #fafafa; /*change background*/
|
||||
--text-color: #333333; /*change text color*/
|
||||
--md-char-color: #c7c5c5; /*change color of meta characetrs like `*` in markdown */
|
||||
--meta-content-color: #5b808d; /*change color of meta contents like image text or link address in markdown */
|
||||
|
||||
--primary-color: #428bca; /* color of primary buttons */
|
||||
--primary-btn-border-color: #285e8e;
|
||||
--primary-btn-text-color: #fff;
|
||||
|
||||
--window-border: 1px solid #eee; /*border for sidebar, etc*/
|
||||
|
||||
--active-file-bg-color: #eee; /*background color if list item in file tree or file list*/
|
||||
--active-file-text-color: inherit;
|
||||
--active-file-border-color: #777;
|
||||
|
||||
--side-bar-bg-color: var(--bg-color); /*change background of sidebar*/
|
||||
--item-hover-bg-color: rgba(
|
||||
229,
|
||||
229,
|
||||
229,
|
||||
0.59
|
||||
); /*background of control items when hover, like menu in sidebar*/
|
||||
--item-hover-text-color: inherit;
|
||||
--monospace: monospace; /*monospace font for codes, fences*/
|
||||
}
|
||||
::selection {
|
||||
background: rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
::-moz-selection {
|
||||
background: rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
::-webkit-selection {
|
||||
background: rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
|
||||
.maize-markdown-body {
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
letter-spacing: 0px;
|
||||
font-family: 'Fira Code', '微软雅黑', 'PingFang SC', 'Microsoft YaHei',
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
/*段落*/
|
||||
.maize-markdown-body p {
|
||||
font-size: 16px;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
margin: 0;
|
||||
line-height: 26px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* todo item 对齐 */
|
||||
.maize-markdown-body input[type='checkbox'] {
|
||||
margin-top: calc(1em - 2px);
|
||||
margin-right: 5px;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
/*标题*/
|
||||
.maize-markdown-body h1,
|
||||
.maize-markdown-body h2,
|
||||
.maize-markdown-body h3,
|
||||
.maize-markdown-body h4,
|
||||
.maize-markdown-body h5,
|
||||
.maize-markdown-body h6 {
|
||||
margin: 0.72em 0;
|
||||
padding: 0px;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
.maize-markdown-body h1 {
|
||||
margin: 1.2em 0;
|
||||
font-size: 2rem;
|
||||
/* text-align: center; */
|
||||
}
|
||||
.maize-markdown-body h2::before {
|
||||
content: '✔';
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.maize-markdown-body h2 {
|
||||
font-size: 1.7rem;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.maize-markdown-body h2 span {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 3px 10px 1px;
|
||||
}
|
||||
|
||||
.maize-markdown-body h3 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.maize-markdown-body h4 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.maize-markdown-body h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.maize-markdown-body h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 列表 */
|
||||
.maize-markdown-body ul,
|
||||
.maize-markdown-body ol {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 40px;
|
||||
color: black;
|
||||
}
|
||||
.maize-markdown-body ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.maize-markdown-body ul ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
.maize-markdown-body ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
/* 列表内容 */
|
||||
.maize-markdown-body li section {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* 字体加粗 */
|
||||
.maize-markdown-body strong::before {
|
||||
content: '「';
|
||||
}
|
||||
|
||||
.maize-markdown-body strong {
|
||||
color: #2196f3;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.maize-markdown-body strong::after {
|
||||
content: '」';
|
||||
}
|
||||
|
||||
/*引用*/
|
||||
.maize-markdown-body blockquote {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 16px;
|
||||
padding: 10px 10px 10px 20px;
|
||||
font-size: 0.9em;
|
||||
background: #e8f4fd;
|
||||
border-left: 3px solid #2196f3;
|
||||
color: #6a737d;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.maize-markdown-body blockquote p {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
/* 超链接 */
|
||||
.maize-markdown-body a {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
color: #2196f3;
|
||||
border-bottom: 1px solid #2196f3;
|
||||
}
|
||||
.maize-markdown-body a:hover {
|
||||
color: #ff5722 !important;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.maize-markdown-body hr {
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
rgba(231, 93, 109, 0.3),
|
||||
rgba(255, 159, 150, 0.75),
|
||||
rgba(255, 216, 181, 0.3)
|
||||
);
|
||||
}
|
||||
|
||||
/* 行内代码 */
|
||||
.maize-markdown-body p code,
|
||||
.maize-markdown-body span code,
|
||||
.maize-markdown-body li code {
|
||||
word-wrap: break-word;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
margin: 0 2px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 文章插图 */
|
||||
.maize-markdown-body img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 4px 12px #84a1a8;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.maize-markdown-body table tr {
|
||||
border: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: white;
|
||||
}
|
||||
.maize-markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.maize-markdown-body table tr th,
|
||||
.maize-markdown-body table tr td {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
.maize-markdown-body table tr th {
|
||||
font-weight: bold;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 上角标 */
|
||||
.maize-markdown-body .md-footnote {
|
||||
font-weight: bold;
|
||||
color: #e49123;
|
||||
}
|
||||
.maize-markdown-body .md-footnote > .md-text:before {
|
||||
content: '[';
|
||||
}
|
||||
.maize-markdown-body .md-footnote > .md-text:after {
|
||||
content: ']';
|
||||
}
|
||||
|
||||
/* 下角标 */
|
||||
.maize-markdown-body .md-def-name {
|
||||
padding-right: 1.8ch;
|
||||
}
|
||||
.maize-markdown-body .md-def-name:before {
|
||||
content: '[';
|
||||
color: #000;
|
||||
}
|
||||
.maize-markdown-body .md-def-name:after {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* 代码块主题 */
|
||||
code,
|
||||
.md-fences {
|
||||
padding: 0.5em;
|
||||
/* border: 1px solid #ccc; */
|
||||
padding: 0.1em;
|
||||
border-radius: 5px;
|
||||
margin-left: 0.2em;
|
||||
margin-right: 0.2em;
|
||||
font-family: 'Fira Code', '微软雅黑', 'PingFang SC', 'Microsoft YaHei',
|
||||
sans-serif;
|
||||
}
|
||||
.md-fences {
|
||||
margin: 0 0 20px;
|
||||
background-color: #292b35;
|
||||
color: rgb(236, 236, 236);
|
||||
/* font-size: 1em; */
|
||||
padding: 0.3em 1em;
|
||||
padding-top: 0.4em;
|
||||
box-shadow: 0px 4px 9px grey;
|
||||
}
|
||||
.CodeMirror div.CodeMirror-cursor {
|
||||
margin-left: 4.6px;
|
||||
border-left: 1px solid #7ba0f0;
|
||||
z-index: 5;
|
||||
}
|
||||
.cm-s-inner div.CodeMirror-selected {
|
||||
background: rgba(113, 124, 180, 0.2);
|
||||
}
|
||||
.CodeMirror-code {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.cm-s-inner .cm-keyword {
|
||||
color: #c792ea;
|
||||
font-weight: 450;
|
||||
}
|
||||
.cm-s-inner .cm-operator {
|
||||
color: #89ddff;
|
||||
}
|
||||
.cm-s-inner .cm-variable-2 {
|
||||
color: #eeffff;
|
||||
}
|
||||
.cm-s-inner .cm-variable-3,
|
||||
.cm-s-inner .cm-type {
|
||||
color: #f07178;
|
||||
}
|
||||
.cm-s-inner .cm-builtin {
|
||||
color: #ffcb6b;
|
||||
}
|
||||
.cm-s-inner .cm-atom {
|
||||
color: #f78c6c;
|
||||
}
|
||||
.cm-s-inner .cm-number {
|
||||
color: #ff5370;
|
||||
}
|
||||
.cm-s-inner .cm-def {
|
||||
color: #82aaff;
|
||||
}
|
||||
.cm-s-inner .cm-string {
|
||||
color: #c3e88d;
|
||||
}
|
||||
.cm-s-inner .cm-string-2 {
|
||||
color: #f07178;
|
||||
}
|
||||
.cm-s-inner .cm-comment {
|
||||
color: #676e95;
|
||||
}
|
||||
.cm-s-inner .cm-variable {
|
||||
color: #f07178;
|
||||
}
|
||||
.cm-s-inner .cm-tag {
|
||||
color: #ff5370;
|
||||
}
|
||||
.cm-s-inner .cm-meta {
|
||||
color: #ffcb6b;
|
||||
}
|
||||
.cm-s-inner .cm-attribute {
|
||||
color: #c792ea;
|
||||
}
|
||||
.cm-s-inner .cm-property {
|
||||
color: #c792ea;
|
||||
}
|
||||
.cm-s-inner .cm-qualifier {
|
||||
color: #decb6b;
|
||||
}
|
||||
.cm-s-inner .cm-variable-3,
|
||||
.cm-s-inner .cm-type {
|
||||
color: #decb6b;
|
||||
}
|
||||
.cm-s-inner .cm-error {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: #e9405c;
|
||||
}
|
||||
|
||||
/*流程图样式*/
|
||||
pre[lang='sequence'],
|
||||
pre[lang='flow'],
|
||||
pre[lang='mermaid'] {
|
||||
background: #ffffff;
|
||||
color: #333333;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="mavonEditor">
|
||||
<mavon-editor
|
||||
ref="md"
|
||||
@imgAdd="$imgAdd"
|
||||
@imgDel="$imgDel"
|
||||
v-model="currentValue"
|
||||
codeStyle="arduino-light"
|
||||
></mavon-editor>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import myMessage from '@/common/message';
|
||||
export default {
|
||||
name: 'Editor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
img_file: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 将图片上传到服务器,返回地址替换到md中
|
||||
$imgAdd(pos, $file) {
|
||||
var formdata = new FormData();
|
||||
formdata.append('image', $file);
|
||||
//将下面上传接口替换为你自己的服务器接口
|
||||
this.$http({
|
||||
url: '/file/upload-md-img',
|
||||
method: 'post',
|
||||
data: formdata,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
}).then((res) => {
|
||||
this.$refs.md.$img2Url(pos, res.data.data.link);
|
||||
this.img_file[res.data.data.link] = res.data.data.filePath;
|
||||
});
|
||||
},
|
||||
$imgDel(pos) {
|
||||
// 删除文件
|
||||
this.$http({
|
||||
url: '/file/delete-md-img',
|
||||
method: 'get',
|
||||
params: {
|
||||
filePath: this.img_file[pos[0]],
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (this.currentValue !== val) {
|
||||
this.currentValue = val;
|
||||
}
|
||||
},
|
||||
currentValue(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.$emit('update:value', newVal);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.mavonEditor {
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
|
@ -1,74 +0,0 @@
|
|||
<template>
|
||||
<textarea ref="editor"></textarea>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Simditor from 'tar-simditor' // 富文本编辑器
|
||||
import 'tar-simditor/styles/simditor.css'
|
||||
import 'tar-simditor-markdown'
|
||||
import 'tar-simditor-markdown/styles/simditor-markdown.css'
|
||||
import './simditor-file-upload'
|
||||
|
||||
export default {
|
||||
name: 'Simditor',
|
||||
props: {
|
||||
toolbar: {
|
||||
type: Array,
|
||||
default: () => ['title', 'bold', 'italic', 'underline', 'fontScale', 'color', 'ol', 'ul', '|', 'link', 'image', 'uploadfile', 'hr', '|', 'indent', 'outdent', 'alignment', '|', 'markdown']
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
editor: null,
|
||||
currentValue: this.value
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.editor = new Simditor({
|
||||
textarea: this.$refs.editor,
|
||||
toolbar: this.toolbar,
|
||||
pasteImage: true,
|
||||
markdown: false,
|
||||
upload: {
|
||||
url: '/api/admin/upload_image/',
|
||||
params: null,
|
||||
fileKey: 'image',//服务器端获取文件数据的参数名
|
||||
connectionCount: 3,
|
||||
leaveConfirm: '正在上传文件'
|
||||
},
|
||||
allowedStyles: {
|
||||
span: ['color']
|
||||
}
|
||||
})
|
||||
this.editor.on('valuechanged', (e, src) => {
|
||||
this.currentValue = this.editor.getValue()
|
||||
})
|
||||
this.editor.on('decorate', (e, src) => {
|
||||
this.currentValue = this.editor.getValue()
|
||||
})
|
||||
|
||||
this.editor.setValue(this.value)
|
||||
},
|
||||
watch: {
|
||||
'value' (val) {
|
||||
if (this.currentValue !== val) {
|
||||
this.currentValue = val
|
||||
this.editor.setValue(val)
|
||||
}
|
||||
},
|
||||
'currentValue' (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.$emit('change', newVal)
|
||||
this.$emit('input', newVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,85 +0,0 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import Simditor from 'tar-simditor'
|
||||
import * as $ from 'jquery'
|
||||
|
||||
var UploadFile,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function (child, parent) {
|
||||
for (var key in parent) {
|
||||
if (__hasProp.call(parent, key)) child[key] = parent[key];
|
||||
}
|
||||
|
||||
function ctor() {
|
||||
this.constructor = child;
|
||||
}
|
||||
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.__super__ = parent.prototype;
|
||||
return child;
|
||||
},
|
||||
__slice = [].slice;
|
||||
|
||||
UploadFile = (function (_super) {
|
||||
__extends(UploadFile, _super);
|
||||
|
||||
UploadFile.i18n = {
|
||||
'zh-CN': {
|
||||
uploadfile: '上传文件'
|
||||
},
|
||||
'en-US': {
|
||||
uploadfile: 'upload file'
|
||||
}
|
||||
};
|
||||
|
||||
UploadFile.prototype.name = 'uploadfile';
|
||||
|
||||
UploadFile.prototype.icon = 'upload';
|
||||
|
||||
function UploadFile() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||||
UploadFile.__super__.constructor.apply(this, args);
|
||||
this._initUpload();
|
||||
}
|
||||
|
||||
UploadFile.prototype._initUpload = function () {
|
||||
this.input = $('<input />', {
|
||||
type: 'file',
|
||||
style: 'position:absolute;top:0;right:0;height:100%;width:100%;opacity:0;filter:alpha(opacity=0);cursor:pointer;'
|
||||
}).prependTo(this.el)
|
||||
var _this = this;
|
||||
this.el.on('click mousedown', 'input[type=file]', function (e) {
|
||||
return e.stopPropagation();
|
||||
}).on('change', 'input[type=file]', function (e) {
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('file', this.files[0]);
|
||||
$.ajax({
|
||||
url: '/api/admin/upload_file',
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false
|
||||
}).done(function (res) {
|
||||
if (!res.success) {
|
||||
alert("upload file failed")
|
||||
} else {
|
||||
let link = '<a target="_blank" className="simditor-attach-link" href="' + res.file_path + '">' + res.file_name + '</a>'
|
||||
_this.editor.setValue(_this.editor.getValue() + link)
|
||||
}
|
||||
}).fail(function (res) {
|
||||
alert("upload file failed")
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return UploadFile;
|
||||
|
||||
})(Simditor.Button);
|
||||
|
||||
Simditor.Toolbar.addButton(UploadFile);
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<el-card shadow :padding="10">
|
||||
<div slot="header">
|
||||
<span class="panel-title" v-if="isContest">{{ title }}</span>
|
||||
<span v-else class="home-title panel-title">{{ title}}</span>
|
||||
<span v-else class="home-title panel-title">{{ title }}</span>
|
||||
<span style="float: right">
|
||||
<el-button
|
||||
v-if="listVisible"
|
||||
|
@ -13,7 +13,12 @@
|
|||
:loading="btnLoading"
|
||||
>Refresh</el-button
|
||||
>
|
||||
<el-button v-else type="primary" icon="el-icon-back" @click="goBack" size="small"
|
||||
<el-button
|
||||
v-else
|
||||
type="primary"
|
||||
icon="el-icon-back"
|
||||
@click="goBack"
|
||||
size="small"
|
||||
>Back</el-button
|
||||
>
|
||||
</span>
|
||||
|
@ -38,16 +43,12 @@
|
|||
|
||||
<div class="info">
|
||||
<span class="date">
|
||||
|
||||
<i class="el-icon-edit"></i>
|
||||
{{ announcement.gmtCreate | localtime }}
|
||||
|
||||
</span>
|
||||
<span class="creator">
|
||||
|
||||
<i class="el-icon-user"></i>
|
||||
{{ announcement.username }}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,9 +67,10 @@
|
|||
<template v-else>
|
||||
<div
|
||||
v-katex
|
||||
v-highlight
|
||||
v-html="announcement.content"
|
||||
key="content"
|
||||
class="content-container markdown-body"
|
||||
class="content-container maize-markdown-body"
|
||||
></div>
|
||||
</template>
|
||||
</transition-group>
|
||||
|
@ -76,11 +78,10 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import api from "@/common/api";
|
||||
import Pagination from "@/components/oj/common/Pagination";
|
||||
|
||||
import api from '@/common/api';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
export default {
|
||||
name: "Announcement",
|
||||
name: 'Announcement',
|
||||
components: {
|
||||
Pagination,
|
||||
},
|
||||
|
@ -90,7 +91,7 @@ export default {
|
|||
total: 0,
|
||||
btnLoading: false,
|
||||
announcements: [],
|
||||
announcement: "",
|
||||
announcement: '',
|
||||
listVisible: true,
|
||||
};
|
||||
},
|
||||
|
@ -120,7 +121,13 @@ export default {
|
|||
},
|
||||
getContestAnnouncementList(page = 1) {
|
||||
this.btnLoading = true;
|
||||
api.getContestAnnouncementList(page, this.limit,this.$route.params.contestID).then(
|
||||
api
|
||||
.getContestAnnouncementList(
|
||||
page,
|
||||
this.limit,
|
||||
this.$route.params.contestID
|
||||
)
|
||||
.then(
|
||||
(res) => {
|
||||
this.btnLoading = false;
|
||||
this.announcements = res.data.data.records;
|
||||
|
@ -133,17 +140,18 @@ export default {
|
|||
},
|
||||
goAnnouncement(announcement) {
|
||||
this.announcement = announcement;
|
||||
this.announcement.content = this.$markDown.render(announcement.content);
|
||||
this.listVisible = false;
|
||||
},
|
||||
goBack() {
|
||||
this.listVisible = true;
|
||||
this.announcement = "";
|
||||
this.announcement = '';
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
if (this.listVisible) {
|
||||
return this.isContest ? "Contest Announcements" : "Announcements";
|
||||
return this.isContest ? 'Contest Announcements' : 'Announcements';
|
||||
} else {
|
||||
return this.announcement.title;
|
||||
}
|
||||
|
@ -165,18 +173,18 @@ export default {
|
|||
list-style: none;
|
||||
padding-bottom: 15px;
|
||||
margin-left: 20px;
|
||||
margin-top:10px;
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid rgba(187, 187, 187, 0.5);
|
||||
border-left: 2px solid #409EFF;
|
||||
border-left: 2px solid #409eff;
|
||||
}
|
||||
/* .announcements-container li:last-child {
|
||||
border-bottom: none;
|
||||
} */
|
||||
.flex-container{
|
||||
.flex-container {
|
||||
text-align: center;
|
||||
}
|
||||
.flex-container .info{
|
||||
.flex-container .info {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
@ -210,8 +218,8 @@ export default {
|
|||
.announcement-animate-enter-active {
|
||||
animation: fadeIn 1s;
|
||||
}
|
||||
ul{
|
||||
list-style-type:none;
|
||||
padding-inline-start:0px;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-inline-start: 0px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -43,6 +43,12 @@ import 'echarts/lib/component/markPoint'
|
|||
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'
|
||||
// 前端所用markdown样式
|
||||
import '@/assets/css/maize.css'
|
||||
Vue.use(mavonEditor)
|
||||
|
||||
Object.keys(filters).forEach(key => { // 注册全局过滤器
|
||||
Vue.filter(key, filters[key])
|
||||
|
@ -61,6 +67,9 @@ Vue.use(SlideVerify) // 滑动验证码组件
|
|||
|
||||
Vue.component('ECharts', ECharts)
|
||||
Vue.prototype.$axios = axios
|
||||
|
||||
Vue.prototype.$markDown = mavonEditor.markdownIt
|
||||
|
||||
Vue.config.productionTip = false
|
||||
new Vue({
|
||||
router,
|
||||
|
|
|
@ -3,19 +3,22 @@
|
|||
<el-card>
|
||||
<div slot="header">
|
||||
<span class="panel-title home-title">
|
||||
{{title}}
|
||||
{{ title }}
|
||||
</span>
|
||||
</div>
|
||||
<el-form label-position="top">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Contest Title" required>
|
||||
<el-input v-model="contest.title" placeholder="Enter the Contest Title"></el-input>
|
||||
<el-input
|
||||
v-model="contest.title"
|
||||
placeholder="Enter the Contest Title"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="Contest Description" required>
|
||||
<Simditor v-model="contest.description"></Simditor>
|
||||
<Editor :value.sync="contest.description"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
|
@ -24,7 +27,8 @@
|
|||
v-model="contest.startTime"
|
||||
@change="changeDuration"
|
||||
type="datetime"
|
||||
placeholder="Enter the Contest Start Time">
|
||||
placeholder="Enter the Contest Start Time"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -34,21 +38,33 @@
|
|||
v-model="contest.endTime"
|
||||
@change="changeDuration"
|
||||
type="datetime"
|
||||
placeholder="Enter the Contest End Time">
|
||||
placeholder="Enter the Contest End Time"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Duration" required>
|
||||
<el-input v-model="durationText" disabled>
|
||||
</el-input>
|
||||
<el-input v-model="durationText" disabled> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Type">
|
||||
<el-radio class="radio" v-model="contest.type" :label="0" :disabled="disableRuleType">ACM</el-radio>
|
||||
<el-radio class="radio" v-model="contest.type" :label="1" :disabled="disableRuleType">OI</el-radio>
|
||||
<el-radio
|
||||
class="radio"
|
||||
v-model="contest.type"
|
||||
:label="0"
|
||||
:disabled="disableRuleType"
|
||||
>ACM</el-radio
|
||||
>
|
||||
<el-radio
|
||||
class="radio"
|
||||
v-model="contest.type"
|
||||
:label="1"
|
||||
:disabled="disableRuleType"
|
||||
>OI</el-radio
|
||||
>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
@ -57,7 +73,8 @@
|
|||
<el-switch
|
||||
v-model="contest.sealRank"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
inactive-color="#ff4949"
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -67,16 +84,29 @@
|
|||
<el-switch
|
||||
v-model="contest.sealRank"
|
||||
active-color="#13ce66"
|
||||
inactive-color="">
|
||||
inactive-color=""
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Seal Rank Time" :required="contest.sealRank" v-show="contest.sealRank">
|
||||
<el-select v-model="seal_rank_time" >
|
||||
<el-option label="比赛结束前半小时" :value="0" :disabled="contest.duration<1800"></el-option>
|
||||
<el-option label="比赛结束前一小时" :value="1" :disabled="contest.duration<3600"></el-option>
|
||||
<el-form-item
|
||||
label="Seal Rank Time"
|
||||
:required="contest.sealRank"
|
||||
v-show="contest.sealRank"
|
||||
>
|
||||
<el-select v-model="seal_rank_time">
|
||||
<el-option
|
||||
label="比赛结束前半小时"
|
||||
:value="0"
|
||||
:disabled="contest.duration < 1800"
|
||||
></el-option>
|
||||
<el-option
|
||||
label="比赛结束前一小时"
|
||||
:value="1"
|
||||
:disabled="contest.duration < 3600"
|
||||
></el-option>
|
||||
<el-option label="比赛全程时间" :value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
@ -84,7 +114,7 @@
|
|||
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Auth" required>
|
||||
<el-select v-model="contest.auth" >
|
||||
<el-select v-model="contest.auth">
|
||||
<el-option label="公开赛" :value="0"></el-option>
|
||||
<el-option label="私有赛" :value="1"></el-option>
|
||||
<el-option label="保护赛" :value="2"></el-option>
|
||||
|
@ -92,8 +122,15 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :md="8" :xs="24">
|
||||
<el-form-item label="Contest Password" v-show="contest.auth!=0" :required="contest.auth!=0">
|
||||
<el-input v-model="contest.pwd" placeholder="Contest Password"></el-input>
|
||||
<el-form-item
|
||||
label="Contest Password"
|
||||
v-show="contest.auth != 0"
|
||||
:required="contest.auth != 0"
|
||||
>
|
||||
<el-input
|
||||
v-model="contest.pwd"
|
||||
placeholder="Contest Password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- <el-col :span="24">
|
||||
|
@ -119,67 +156,69 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/common/api'
|
||||
import Simditor from '@/components/admin/Simditor.vue'
|
||||
import time from '@/common/time'
|
||||
import moment from 'moment'
|
||||
import { mapGetters } from "vuex";
|
||||
import myMessage from '@/common/message'
|
||||
export default {
|
||||
import api from '@/common/api';
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import time from '@/common/time';
|
||||
import moment from 'moment';
|
||||
import { mapGetters } from 'vuex';
|
||||
import myMessage from '@/common/message';
|
||||
export default {
|
||||
name: 'CreateContest',
|
||||
components: {
|
||||
Simditor
|
||||
Editor,
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
title: 'Create Contest',
|
||||
disableRuleType: false,
|
||||
durationText:'', // 比赛时长文本表示
|
||||
seal_rank_time:0, // 当开启封榜模式,即实时榜单关闭时,可选择前半小时,前一小时,全程封榜,默认半小时1800s
|
||||
durationText: '', // 比赛时长文本表示
|
||||
seal_rank_time: 0, // 当开启封榜模式,即实时榜单关闭时,可选择前半小时,前一小时,全程封榜,默认半小时1800s
|
||||
contest: {
|
||||
title: '',
|
||||
description: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
duration:0,
|
||||
duration: 0,
|
||||
type: 0,
|
||||
password: '',
|
||||
sealRank: true,
|
||||
sealRankTime:'',//封榜时间
|
||||
sealRankTime: '', //封榜时间
|
||||
auth: 0,
|
||||
// allowed_ip_ranges: [{
|
||||
// value: ''
|
||||
// }]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.$route.name === 'admin-edit-contest') {
|
||||
this.title = 'Edit Contest'
|
||||
this.disableRuleType = true
|
||||
this.title = 'Edit Contest';
|
||||
this.disableRuleType = true;
|
||||
this.getContestByCid();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
if (this.$route.name === 'admin-edit-contest') {
|
||||
this.title = 'Edit Contest'
|
||||
this.disableRuleType = true
|
||||
this.title = 'Edit Contest';
|
||||
this.disableRuleType = true;
|
||||
this.getContestByCid();
|
||||
}else{
|
||||
this.title = 'Create Contest'
|
||||
this.disableRuleType = false
|
||||
this.contest = []
|
||||
}
|
||||
} else {
|
||||
this.title = 'Create Contest';
|
||||
this.disableRuleType = false;
|
||||
this.contest = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["userInfo"]),
|
||||
...mapGetters(['userInfo']),
|
||||
},
|
||||
methods: {
|
||||
getContestByCid(){
|
||||
api.admin_getContest(this.$route.params.contestId).then(res => {
|
||||
let data = res.data.data
|
||||
getContestByCid() {
|
||||
api
|
||||
.admin_getContest(this.$route.params.contestId)
|
||||
.then((res) => {
|
||||
let data = res.data.data;
|
||||
// let ranges = []
|
||||
// for (let v of data.allowed_ip_ranges) {
|
||||
// ranges.push({value: v})
|
||||
|
@ -188,14 +227,18 @@
|
|||
// ranges.push({value: ''})
|
||||
// }
|
||||
// data.allowed_ip_ranges = ranges
|
||||
this.contest = data
|
||||
this.changeDuration()
|
||||
this.contest = data;
|
||||
this.changeDuration();
|
||||
// 封榜时间转换
|
||||
let halfHour = moment(this.contest.endTime).subtract(1800,'seconds').toString()
|
||||
let oneHour = moment(this.contest.endTime).subtract(3600,'seconds').toString()
|
||||
let allHour = moment(this.contest.startTime).toString()
|
||||
let sealRankTime = moment(this.contest.sealRankTime).toString()
|
||||
switch(sealRankTime){
|
||||
let halfHour = moment(this.contest.endTime)
|
||||
.subtract(1800, 'seconds')
|
||||
.toString();
|
||||
let oneHour = moment(this.contest.endTime)
|
||||
.subtract(3600, 'seconds')
|
||||
.toString();
|
||||
let allHour = moment(this.contest.startTime).toString();
|
||||
let sealRankTime = moment(this.contest.sealRankTime).toString();
|
||||
switch (sealRankTime) {
|
||||
case halfHour:
|
||||
this.seal_rank_time = 0;
|
||||
break;
|
||||
|
@ -206,24 +249,33 @@
|
|||
this.seal_rank_time = 2;
|
||||
break;
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
saveContest () {
|
||||
let funcName = this.$route.name === 'admin-edit-contest' ? 'admin_editContest' : 'admin_createContest'
|
||||
saveContest() {
|
||||
let funcName =
|
||||
this.$route.name === 'admin-edit-contest'
|
||||
? 'admin_editContest'
|
||||
: 'admin_createContest';
|
||||
|
||||
switch(this.seal_rank_time){
|
||||
switch (this.seal_rank_time) {
|
||||
case 0: // 结束前半小时
|
||||
this.contest.sealRankTime = moment(this.contest.endTime).subtract(1800,'seconds');
|
||||
this.contest.sealRankTime = moment(this.contest.endTime).subtract(
|
||||
1800,
|
||||
'seconds'
|
||||
);
|
||||
break;
|
||||
case 1: // 结束前一小时
|
||||
this.contest.sealRankTime = moment(this.contest.endTime).subtract(3600,'seconds');
|
||||
this.contest.sealRankTime = moment(this.contest.endTime).subtract(
|
||||
3600,
|
||||
'seconds'
|
||||
);
|
||||
break;
|
||||
case 2: // 全程
|
||||
this.contest.sealRankTime = moment(this.contest.startTime);
|
||||
}
|
||||
let data = Object.assign({}, this.contest)
|
||||
let data = Object.assign({}, this.contest);
|
||||
// let ranges = []
|
||||
// for (let v of data.allowed_ip_ranges) {
|
||||
// if (v.value !== '') {
|
||||
|
@ -231,28 +283,32 @@
|
|||
// }
|
||||
// }
|
||||
// data.allowed_ip_ranges = ranges
|
||||
if(funcName === 'admin_createContest'){
|
||||
data['uid'] = this.userInfo.uid
|
||||
data['author'] = this.userInfo.username
|
||||
if (funcName === 'admin_createContest') {
|
||||
data['uid'] = this.userInfo.uid;
|
||||
data['author'] = this.userInfo.username;
|
||||
}
|
||||
|
||||
api[funcName](data).then((res) => {
|
||||
myMessage.success(res.data.msg)
|
||||
this.$router.push({name: 'admin-contest-list', query: {refresh: 'true'}})
|
||||
}).catch(() => {
|
||||
api[funcName](data)
|
||||
.then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
this.$router.push({
|
||||
name: 'admin-contest-list',
|
||||
query: { refresh: 'true' },
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
changeDuration(){
|
||||
changeDuration() {
|
||||
let start = this.contest.startTime;
|
||||
let end = this.contest.endTime
|
||||
let durationMS = time.durationMs(start,end);
|
||||
if(durationMS<0){
|
||||
this.durationText = '比赛起始时间不应该晚于结束时间!'
|
||||
let end = this.contest.endTime;
|
||||
let durationMS = time.durationMs(start, end);
|
||||
if (durationMS < 0) {
|
||||
this.durationText = '比赛起始时间不应该晚于结束时间!';
|
||||
this.contest.duration = 0;
|
||||
return;
|
||||
}
|
||||
if(start!=''&&end!=''){
|
||||
this.durationText = time.duration(start,end);
|
||||
if (start != '' && end != '') {
|
||||
this.durationText = time.duration(start, end);
|
||||
this.contest.duration = durationMS;
|
||||
}
|
||||
},
|
||||
|
@ -266,5 +322,5 @@
|
|||
// }
|
||||
// }
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,13 @@
|
|||
<span class="panel-title home-title">Announcement</span>
|
||||
</div>
|
||||
<div class="create">
|
||||
<el-button type="primary" size="small" @click="openAnnouncementDialog(null)" icon="el-icon-plus">Create</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="openAnnouncementDialog(null)"
|
||||
icon="el-icon-plus"
|
||||
>Create</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="list">
|
||||
<vxe-table
|
||||
|
@ -15,60 +21,70 @@
|
|||
auto-resize
|
||||
stripe
|
||||
>
|
||||
<vxe-table-column
|
||||
min-width="50"
|
||||
field="id"
|
||||
title="ID">
|
||||
<vxe-table-column min-width="50" field="id" title="ID">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="title"
|
||||
title="Title">
|
||||
<vxe-table-column min-width="150" field="title" title="Title">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtCreate"
|
||||
title="Create Time">
|
||||
<template v-slot="{row}">
|
||||
title="Create Time"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtCreate | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="gmtModified"
|
||||
title="Last Update Time">
|
||||
<template v-slot="{row}">
|
||||
{{row.gmtModified | localtime }}
|
||||
title="Last Update Time"
|
||||
>
|
||||
<template v-slot="{ row }">
|
||||
{{ row.gmtModified | localtime }}
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="150"
|
||||
field="username"
|
||||
title="Author">
|
||||
<vxe-table-column min-width="150" field="username" title="Author">
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
min-width="100"
|
||||
field="status"
|
||||
title="Visible">
|
||||
<template v-slot="{row}">
|
||||
<el-switch v-model="row.status"
|
||||
<vxe-table-column min-width="100" field="status" title="Visible">
|
||||
<template v-slot="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
active-text=""
|
||||
inactive-text=""
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
@change="handleVisibleSwitch(row)">
|
||||
@change="handleVisibleSwitch(row)"
|
||||
>
|
||||
</el-switch>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
<vxe-table-column
|
||||
title="Option"
|
||||
min-width="150">
|
||||
<vxe-table-column title="Option" min-width="150">
|
||||
<template v-slot="row">
|
||||
<el-tooltip class="item" effect="dark" content="编辑公告" placement="top">
|
||||
<el-button icon="el-icon-edit-outline" @click.native="openAnnouncementDialog(row)" size="mini" type="primary"></el-button>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="编辑公告"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-edit-outline"
|
||||
@click.native="openAnnouncementDialog(row.row)"
|
||||
size="mini"
|
||||
type="primary"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="删除公告" placement="top">
|
||||
<el-button icon="el-icon-delete-solid" @click.native="deleteAnnouncement(row.row.id)" size="mini" type="danger"></el-button>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="删除公告"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-delete-solid"
|
||||
@click.native="deleteAnnouncement(row.row.id)"
|
||||
size="mini"
|
||||
type="danger"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</vxe-table-column>
|
||||
|
@ -81,24 +97,31 @@
|
|||
layout="prev, pager, next"
|
||||
@current-change="currentChange"
|
||||
:page-size="pageSize"
|
||||
:total="total">
|
||||
:total="total"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!--编辑公告对话框-->
|
||||
<el-dialog :title="announcementDialogTitle" :visible.sync="showEditAnnouncementDialog" :fullscreen="true"
|
||||
@open="onOpenEditDialog">
|
||||
<el-dialog
|
||||
:title="announcementDialogTitle"
|
||||
:visible.sync="showEditAnnouncementDialog"
|
||||
:fullscreen="true"
|
||||
@open="onOpenEditDialog"
|
||||
>
|
||||
<el-form label-position="top" :model="announcement">
|
||||
<el-form-item label="公告标题" required>
|
||||
<el-input
|
||||
v-model="announcement.title"
|
||||
placeholder="请输入公告标题" class="title-input">
|
||||
placeholder="请输入公告标题"
|
||||
class="title-input"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="公告内容" required>
|
||||
<Simditor v-model="announcement.content"></Simditor>
|
||||
<Editor :value.sync="announcement.content"></Editor>
|
||||
</el-form-item>
|
||||
<div class="visible-box">
|
||||
<span>是否显示</span>
|
||||
|
@ -107,29 +130,36 @@
|
|||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
active-text=""
|
||||
inactive-text="">
|
||||
inactive-text=""
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="danger" @click.native="showEditAnnouncementDialog = false">Cancel</el-button>
|
||||
<el-button type="primary" @click.native="submitAnnouncement">Save</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click.native="showEditAnnouncementDialog = false"
|
||||
>Cancel</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="submitAnnouncement"
|
||||
>Save</el-button
|
||||
>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Simditor from '@/components/admin/Simditor.vue'
|
||||
import api from '@/common/api'
|
||||
import myMessage from '@/common/message'
|
||||
import { mapGetters } from "vuex";
|
||||
export default {
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'announcement',
|
||||
components: {
|
||||
Simditor
|
||||
Editor,
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
contestID: '',
|
||||
// 显示编辑公告对话框
|
||||
|
@ -144,182 +174,200 @@
|
|||
// 公告 (new | edit) model
|
||||
|
||||
announcement: {
|
||||
id:null,
|
||||
id: null,
|
||||
title: '',
|
||||
content:'',
|
||||
content: '',
|
||||
status: 0,
|
||||
content: '',
|
||||
uid:'',
|
||||
uid: '',
|
||||
},
|
||||
// 对话框标题
|
||||
announcementDialogTitle: 'Edit Announcement',
|
||||
// 是否显示loading
|
||||
loading: false,
|
||||
// 当前页码
|
||||
currentPage: 0
|
||||
}
|
||||
currentPage: 0,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.init()
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
this.contestID = this.$route.params.contestId
|
||||
init() {
|
||||
this.contestID = this.$route.params.contestId;
|
||||
if (this.contestID) {
|
||||
this.getContestAnnouncementList(1)
|
||||
this.getContestAnnouncementList(1);
|
||||
} else {
|
||||
this.getAnnouncementList(1)
|
||||
this.getAnnouncementList(1);
|
||||
}
|
||||
},
|
||||
// 切换页码回调
|
||||
currentChange (page) {
|
||||
this.currentPage = page
|
||||
currentChange(page) {
|
||||
this.currentPage = page;
|
||||
if (this.contestID) {
|
||||
this.getContestAnnouncementList(page)
|
||||
}else{
|
||||
this.getAnnouncementList(page)
|
||||
this.getContestAnnouncementList(page);
|
||||
} else {
|
||||
this.getAnnouncementList(page);
|
||||
}
|
||||
},
|
||||
|
||||
getAnnouncementList (page) {
|
||||
this.loading = true
|
||||
api.admin_getAnnouncementList(page, this.pageSize).then(res => {
|
||||
this.loading = false
|
||||
this.total = res.data.data.total
|
||||
this.announcementList = res.data.data.records
|
||||
}, res => {
|
||||
this.loading = false
|
||||
})
|
||||
getAnnouncementList(page) {
|
||||
this.loading = true;
|
||||
api.admin_getAnnouncementList(page, this.pageSize).then(
|
||||
(res) => {
|
||||
this.loading = false;
|
||||
this.total = res.data.data.total;
|
||||
this.announcementList = res.data.data.records;
|
||||
},
|
||||
getContestAnnouncementList (page) {
|
||||
this.loading = true
|
||||
api.admin_getContestAnnouncementList(this.contestID,page,this.pageSize).then(res => {
|
||||
this.loading = false
|
||||
this.total = res.data.data.total
|
||||
this.announcementList = res.data.data.records
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
(res) => {
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
getContestAnnouncementList(page) {
|
||||
this.loading = true;
|
||||
api
|
||||
.admin_getContestAnnouncementList(this.contestID, page, this.pageSize)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
this.total = res.data.data.total;
|
||||
this.announcementList = res.data.data.records;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 打开编辑对话框的回调
|
||||
onOpenEditDialog () {
|
||||
onOpenEditDialog() {
|
||||
// todo 优化
|
||||
// 暂时解决 文本编辑器显示异常bug
|
||||
setTimeout(() => {
|
||||
if (document.createEvent) {
|
||||
let event = document.createEvent('HTMLEvents')
|
||||
event.initEvent('resize', true, true)
|
||||
window.dispatchEvent(event)
|
||||
let event = document.createEvent('HTMLEvents');
|
||||
event.initEvent('resize', true, true);
|
||||
window.dispatchEvent(event);
|
||||
} else if (document.createEventObject) {
|
||||
window.fireEvent('onresize')
|
||||
window.fireEvent('onresize');
|
||||
}
|
||||
}, 0)
|
||||
}, 0);
|
||||
},
|
||||
// 提交编辑
|
||||
// 默认传入MouseEvent
|
||||
submitAnnouncement (data = undefined) {
|
||||
let funcName = ''
|
||||
submitAnnouncement(data = undefined) {
|
||||
let funcName = '';
|
||||
if (!data.id) {
|
||||
data = this.announcement
|
||||
data = this.announcement;
|
||||
}
|
||||
let requestData;
|
||||
if (this.contestID) {
|
||||
let announcement = {
|
||||
announcement:data,
|
||||
cid:this.contestID
|
||||
}
|
||||
requestData = announcement
|
||||
funcName = this.mode === 'edit' ? 'admin_updateContestAnnouncement' : 'admin_createContestAnnouncement'
|
||||
announcement: data,
|
||||
cid: this.contestID,
|
||||
};
|
||||
requestData = announcement;
|
||||
funcName =
|
||||
this.mode === 'edit'
|
||||
? 'admin_updateContestAnnouncement'
|
||||
: 'admin_createContestAnnouncement';
|
||||
} else {
|
||||
funcName = this.mode === 'edit' ? 'admin_updateAnnouncement' : 'admin_createAnnouncement'
|
||||
requestData = data
|
||||
funcName =
|
||||
this.mode === 'edit'
|
||||
? 'admin_updateAnnouncement'
|
||||
: 'admin_createAnnouncement';
|
||||
requestData = data;
|
||||
}
|
||||
api[funcName](requestData).then(res => {
|
||||
this.showEditAnnouncementDialog = false
|
||||
api[funcName](requestData)
|
||||
.then((res) => {
|
||||
this.showEditAnnouncementDialog = false;
|
||||
myMessage.success(res.data.msg);
|
||||
this.init()
|
||||
}).catch()
|
||||
this.init();
|
||||
})
|
||||
.catch();
|
||||
},
|
||||
|
||||
// 删除公告
|
||||
deleteAnnouncement (announcementId) {
|
||||
deleteAnnouncement(announcementId) {
|
||||
this.$confirm('你确定要删除该公告?', 'Warning', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
// then 为确定
|
||||
this.loading = true
|
||||
let funcName = this.contestID ? 'admin_deleteContestAnnouncement' : 'admin_deleteAnnouncement'
|
||||
api[funcName](announcementId).then(res => {
|
||||
this.loading = true
|
||||
this.loading = true;
|
||||
let funcName = this.contestID
|
||||
? 'admin_deleteContestAnnouncement'
|
||||
: 'admin_deleteAnnouncement';
|
||||
api[funcName](announcementId).then((res) => {
|
||||
this.loading = true;
|
||||
myMessage.success(res.data.msg);
|
||||
this.init()
|
||||
this.init();
|
||||
});
|
||||
})
|
||||
}).catch(() => {
|
||||
.catch(() => {
|
||||
// catch 为取消
|
||||
this.loading = false
|
||||
})
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
openAnnouncementDialog (row) {
|
||||
this.showEditAnnouncementDialog = true
|
||||
openAnnouncementDialog(row) {
|
||||
this.showEditAnnouncementDialog = true;
|
||||
if (row !== null) {
|
||||
this.announcementDialogTitle = 'Edit Announcement'
|
||||
this.announcement = Object.assign({},row.data[0])
|
||||
this.mode = 'edit'
|
||||
this.announcementDialogTitle = 'Edit Announcement';
|
||||
this.announcement = Object.assign({}, row);
|
||||
this.mode = 'edit';
|
||||
} else {
|
||||
this.announcementDialogTitle = 'Create Announcement'
|
||||
this.announcement.title = ''
|
||||
this.announcement.status = 0
|
||||
this.announcement.content = ''
|
||||
this.announcement.uid = this.userInfo.uid
|
||||
this.announcement.username = this.userInfo.username
|
||||
this.mode = 'create'
|
||||
this.announcementDialogTitle = 'Create Announcement';
|
||||
this.announcement.title = '';
|
||||
this.announcement.status = 0;
|
||||
this.announcement.content = '';
|
||||
this.announcement.uid = this.userInfo.uid;
|
||||
this.announcement.username = this.userInfo.username;
|
||||
this.mode = 'create';
|
||||
}
|
||||
},
|
||||
handleVisibleSwitch (row) {
|
||||
this.mode = 'edit'
|
||||
handleVisibleSwitch(row) {
|
||||
this.mode = 'edit';
|
||||
this.submitAnnouncement({
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
content: row.content,
|
||||
status: row.status,
|
||||
uid:row.uid
|
||||
})
|
||||
}
|
||||
uid: row.uid,
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route () {
|
||||
this.init()
|
||||
}
|
||||
$route() {
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["userInfo"]),
|
||||
}
|
||||
}
|
||||
...mapGetters(['userInfo']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title-input {
|
||||
.title-input {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.visible-box {
|
||||
.visible-box {
|
||||
margin-top: 10px;
|
||||
width: 205px;
|
||||
float: left;
|
||||
}
|
||||
.visible-box span{
|
||||
margin-right:10px ;
|
||||
}
|
||||
.el-form-item {
|
||||
margin-bottom: 2px!important;
|
||||
}
|
||||
/deep/.el-dialog__body {
|
||||
padding-top: 0!important;
|
||||
}
|
||||
.create{
|
||||
}
|
||||
.visible-box span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.el-form-item {
|
||||
margin-bottom: 2px !important;
|
||||
}
|
||||
/deep/.el-dialog__body {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.create {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="description" label="Description" required>
|
||||
<Simditor v-model="problem.description"></Simditor>
|
||||
<Editor :value.sync="problem.description"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -107,7 +107,7 @@
|
|||
label="Input Description"
|
||||
required
|
||||
>
|
||||
<Simditor v-model="problem.input"></Simditor>
|
||||
<Editor :value.sync="problem.input"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
|
@ -116,7 +116,7 @@
|
|||
label="Output Description"
|
||||
required
|
||||
>
|
||||
<Simditor v-model="problem.output"></Simditor>
|
||||
<Editor :value.sync="problem.output"></Editor>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -322,7 +322,7 @@
|
|||
</template>
|
||||
|
||||
<el-form-item style="margin-top: 20px" label="Hint">
|
||||
<Simditor v-model="problem.hint"></Simditor>
|
||||
<Editor :value.sync="problem.hint"></Editor>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
|
@ -350,6 +350,7 @@
|
|||
|
||||
<el-switch
|
||||
v-model="isUploadTestCase"
|
||||
@change="changeSampleUploadMethod"
|
||||
active-text="Use Upload File"
|
||||
inactive-text="Use Manual Input"
|
||||
style="margin: 10px 0"
|
||||
|
@ -506,7 +507,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Simditor from '@/components/admin/Simditor';
|
||||
import Editor from '@/components/admin/Editor';
|
||||
import Accordion from '@/components/admin/Accordion';
|
||||
import CodeMirror from '@/components/admin/CodeMirror';
|
||||
import utils from '@/common/utils';
|
||||
|
@ -517,9 +518,9 @@ import { OJ_NAME } from '@/common/constants';
|
|||
export default {
|
||||
name: 'Problem',
|
||||
components: {
|
||||
Simditor,
|
||||
Accordion,
|
||||
CodeMirror,
|
||||
Editor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -727,9 +728,11 @@ export default {
|
|||
this.problemLanguages.push(Languages[i].name);
|
||||
}
|
||||
});
|
||||
if (!this.isUploadTestCase) {
|
||||
api.admin_getProblemCases(this.pid).then((res) => {
|
||||
this.problemSamples = res.data.data;
|
||||
});
|
||||
}
|
||||
api.admin_getProblemTags(this.pid).then((res) => {
|
||||
this.problemTags = res.data.data;
|
||||
});
|
||||
|
@ -740,6 +743,13 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
changeSampleUploadMethod() {
|
||||
if (!this.isUploadTestCase && this.problemSamples.length == 0) {
|
||||
api.admin_getProblemCases(this.pid).then((res) => {
|
||||
this.problemSamples = res.data.data;
|
||||
});
|
||||
}
|
||||
},
|
||||
switchSpj() {
|
||||
if (this.testCaseUploaded) {
|
||||
this.$confirm(
|
||||
|
@ -773,6 +783,9 @@ export default {
|
|||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
changeContent(newVal) {
|
||||
this.announcement.content = newVal;
|
||||
},
|
||||
resetTestCase() {
|
||||
this.testCaseUploaded = false;
|
||||
this.problem.testCaseScore = [];
|
||||
|
|
|
@ -53,7 +53,10 @@
|
|||
</el-button>
|
||||
</div>
|
||||
<div class="contest-description">
|
||||
<blockquote v-html="contest.description"></blockquote>
|
||||
<blockquote
|
||||
v-html="contest.description"
|
||||
class="maize-markdown-body"
|
||||
></blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
<el-tab-pane name="ContestDetails" lazy>
|
||||
<span slot="label"><i class="el-icon-s-home"></i> Overview</span>
|
||||
<el-card class="box-card">
|
||||
<div v-html="contest.description" class="markdown-body"></div>
|
||||
<div v-html="contest.description" class="maize-markdown-body"></div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
|
||||
|
|
|
@ -157,19 +157,14 @@
|
|||
<el-tag
|
||||
effect="dark"
|
||||
:color="submissionStatus.color"
|
||||
@click.native="
|
||||
handleRoute('/submission-detail/' + submissionId)
|
||||
"
|
||||
@click.native="reSubmit(submissionId)"
|
||||
>
|
||||
<i
|
||||
class="el-icon-refresh"
|
||||
@click="reSubmit(submissionId)"
|
||||
></i>
|
||||
<i class="el-icon-refresh"></i>
|
||||
{{ submissionStatus.text }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
v-else-if="
|
||||
!this.contestID ||
|
||||
(this.contestID &&
|
||||
ContestRealTimePermission &&
|
||||
|
@ -348,7 +343,7 @@ export default {
|
|||
language: 'C',
|
||||
isRemote: false,
|
||||
|
||||
theme: 'material',
|
||||
theme: 'solarized',
|
||||
submissionId: '',
|
||||
submitted: false,
|
||||
submitPwdVisible: false,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<span class="title">{{ status.statusName }}</span>
|
||||
</template>
|
||||
<template slot>
|
||||
<div v-if="isCE || isSE" class="content">
|
||||
<div v-if="isCE || isSE || isSF" class="content">
|
||||
<pre>{{ submission.errorMessage }}</pre>
|
||||
</div>
|
||||
<div v-else class="content">
|
||||
|
@ -331,6 +331,9 @@ export default {
|
|||
isSE() {
|
||||
return this.submission.status === JUDGE_STATUS_RESERVE.se;
|
||||
},
|
||||
isSF() {
|
||||
return this.submission.status === JUDGE_STATUS_RESERVE.sf;
|
||||
},
|
||||
isAdminRole() {
|
||||
return this.$store.getters.isAdminRole;
|
||||
},
|
||||
|
|
|
@ -341,6 +341,8 @@ export default {
|
|||
|
||||
// 加入待重判列表
|
||||
this.needCheckSubmitIds[row.submitId] = row.index;
|
||||
|
||||
this.checkStatusNum = 0;
|
||||
if (!this.autoCheckOpen) {
|
||||
// 如果当前未开启自动检查提交状态的定时任务,则开启
|
||||
this.checkSubmissionsStatus();
|
||||
|
@ -541,6 +543,7 @@ export default {
|
|||
|
||||
// 加入待重判列表
|
||||
this.needCheckSubmitIds[row.submitId] = row.index;
|
||||
this.checkStatusNum = 0;
|
||||
if (!this.autoCheckOpen) {
|
||||
// 如果当前未开启自动检查提交状态的定时任务,则开启
|
||||
this.checkSubmissionsStatus();
|
||||
|
|
Loading…
Reference in New Issue