增加对GYM题目的Vjudge支持

This commit is contained in:
Himit_ZH 2021-11-06 23:25:42 +08:00
parent ff87c1898b
commit af9811d036
18 changed files with 274 additions and 90 deletions

View File

@ -28,9 +28,10 @@ public class CorsConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// /api/public/img/** /api/public/file/**
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**",Constants.File.FILE_API.getPath() + "**")
registry.addResourceHandler(Constants.File.IMG_API.getPath() + "**", Constants.File.FILE_API.getPath() + "**")
.addResourceLocations("file:" + Constants.File.USER_AVATAR_FOLDER.getPath() + File.separator,
"file:" + Constants.File.MARKDOWN_FILE_FOLDER.getPath() + File.separator,
"file:" + Constants.File.HOME_CAROUSEL_FOLDER.getPath() + File.separator);
"file:" + Constants.File.HOME_CAROUSEL_FOLDER.getPath() + File.separator,
"file:" + Constants.File.PROBLEM_FILE_FOLDER.getPath() + File.separator);
}
}

View File

@ -67,14 +67,14 @@ public class CommonController {
@GetMapping("/get-all-problem-tags")
public CommonResult getAllProblemTagsList(@RequestParam(value = "oj",defaultValue = "ME")String oj) {
public CommonResult getAllProblemTagsList(@RequestParam(value = "oj", defaultValue = "ME") String oj) {
List<Tag> tagList;
oj = oj.toUpperCase();
if (oj.equals("ALL")){
if (oj.equals("ALL")) {
tagList = tagService.list();
}else {
} else {
QueryWrapper<Tag> tagQueryWrapper = new QueryWrapper<>();
tagQueryWrapper.eq( "oj", oj);
tagQueryWrapper.eq("oj", oj);
tagList = tagService.list(tagQueryWrapper);
}
if (tagList != null) {
@ -110,6 +110,10 @@ public class CommonController {
}
}
if (OJ.equals("GYM")) { // GYM用与CF一样的编程语言列表
OJ = "CF";
}
QueryWrapper<Language> queryWrapper = new QueryWrapper<>();
// 获取对应OJ支持的语言列表
queryWrapper.eq(all != null && !all, "oj", OJ);
@ -143,7 +147,7 @@ public class CommonController {
if (codeTemplates != null && codeTemplates.size() > 0) {
return CommonResult.successResponse(codeTemplates, "获取该题目的代码模板列表成功!");
} else {
return CommonResult.successResponse(codeTemplates,"获取该题目的代码模板列表失败!");
return CommonResult.successResponse(codeTemplates, "获取该题目的代码模板列表失败!");
}
}

View File

@ -23,6 +23,22 @@ public class CFProblemStrategy extends ProblemStrategy {
public static final String HOST = "https://codeforces.com";
public static final String PROBLEM_URL = "/problemset/problem/%s/%s";
public String getJudgeName() {
return JUDGE_NAME;
}
public String getProblemUrl(String contestId, String problemNum) {
return HOST + String.format(PROBLEM_URL, contestId, problemNum);
}
public String getProblemSource(String html, String problemId, String contestId, String problemNum) {
return String.format("<p>Problem<a style='color:#1A5CC8' href='https://codeforces.com/problemset/problem/%s/%s'>%s</a></p><p>" +
"Contest" + ReUtil.get("(<a[^<>]+/contest/\\d+\">.+?</a>)", html, 1)
.replace("/contest", HOST + "/contest")
.replace("color: black", "color: #009688;") + "</p>",
contestId, problemNum, getJudgeName() + "-" + problemId);
}
@Override
public RemoteProblemInfo getProblemInfo(String problemId, String author) throws Exception {
@ -40,12 +56,11 @@ public class CFProblemStrategy extends ProblemStrategy {
throw new Exception("Codeforces的题号格式错误");
}
String url = HOST + String.format(PROBLEM_URL, contestId, problemNum);
String html = HttpUtil.get(url);
String html = HttpUtil.get(getProblemUrl(contestId, problemNum));
Problem info = new Problem();
info.setProblemId(JUDGE_NAME + "-" + problemId);
info.setProblemId(getJudgeName() + "-" + problemId);
info.setTitle(ReUtil.get("<div class=\"title\">\\s*" + problemNum + "\\. ([\\s\\S]*?)</div>", html, 1).trim());
@ -70,12 +85,18 @@ public class CFProblemStrategy extends ProblemStrategy {
tmpDesc = ReUtil.get("<div class=\"input-file\">([\\s\\S]*?)</div><div class=\"input-specification", html, 1);
}
if (StringUtils.isEmpty(tmpDesc)){
if (StringUtils.isEmpty(tmpDesc)) {
// 交互题
tmpDesc = ReUtil.get("standard output\\s*</div>\\s*</div>\\s*<div>([\\s\\S]*?)</div>\\s*<div>\\s*<div class=\"section-title", html, 1);
}
if (!StringUtils.isEmpty(tmpDesc)){
if (StringUtils.isEmpty(tmpDesc)) {
// 单单只有题面描述
tmpDesc = ReUtil.get("standard output\\s*</div>\\s*</div>\\s*<div>([\\s\\S]*?)</div>",
html, 1);
}
if (!StringUtils.isEmpty(tmpDesc)) {
tmpDesc = tmpDesc.replaceAll("\\$\\$\\$", "\\$")
.replaceAll("src=\"../../", "src=\"" + HOST + "/")
.trim();
@ -85,20 +106,20 @@ public class CFProblemStrategy extends ProblemStrategy {
String inputDesc = ReUtil.get("<div class=\"section-title\">\\s*Input\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"output-specification\">", html, 1);
if (StringUtils.isEmpty(inputDesc)){
if (StringUtils.isEmpty(inputDesc)) {
inputDesc = ReUtil.get("<div class=\"section-title\">\\s*Interaction\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"sample-tests\">", html, 1);
}
if (StringUtils.isEmpty(inputDesc)){
if (StringUtils.isEmpty(inputDesc)) {
inputDesc = ReUtil.get("<div class=\"input-specification\">\\s*<div class=\"section-title\">\\s*Input\\s*</div>([\\s\\S]*?)</div>", html, 1);
}
if (!StringUtils.isEmpty(inputDesc)){
if (!StringUtils.isEmpty(inputDesc)) {
inputDesc = inputDesc.replaceAll("\\$\\$\\$", "\\$").trim();
}
info.setInput(inputDesc);
String outputDesc = ReUtil.get("<div class=\"section-title\">\\s*Output\\s*</div>([\\s\\S]*?)</div>\\s*<div class=\"sample-tests\">", html, 1);
if (!StringUtils.isEmpty(outputDesc)){
if (!StringUtils.isEmpty(outputDesc)) {
outputDesc = outputDesc.replaceAll("\\$\\$\\$", "\\$").trim();
}
info.setOutput(outputDesc);
@ -134,10 +155,7 @@ public class CFProblemStrategy extends ProblemStrategy {
info.setIsRemote(true);
info.setSource(String.format("<p>Problem<a style='color:#1A5CC8' href='https://codeforces.com/problemset/problem/%s/%s'>%s</a></p><p>" +
"Contest" + ReUtil.get("(<a[^<>]+/contest/\\d+\">.+?</a>)", html, 1).replace("/contest", HOST + "/contest")
.replace("color: black", "color: #009688;") + "</p>",
contestId, problemNum, JUDGE_NAME + "-" + problemId));
info.setSource(getProblemSource(html, problemId, contestId, problemNum));
info.setType(0)
.setAuth(1)

View File

@ -0,0 +1,103 @@
package top.hcode.hoj.crawler.problem;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;
import top.hcode.hoj.pojo.entity.Problem;
import top.hcode.hoj.utils.Constants;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: Himit_ZH
* @Date: 2021/11/6 11:35
* @Description:
*/
public class GYMProblemStrategy extends CFProblemStrategy {
public static final String IMAGE_HOST = "https://codeforces.ml";
@Override
public String getJudgeName() {
return "GYM";
}
@Override
public String getProblemUrl(String contestId, String problemNum) {
String problemUrl = "/gym/%s/problem/%s";
return HOST + String.format(problemUrl, contestId, problemNum);
}
@Override
public String getProblemSource(String html, String problemId, String contestNum, String problemNum) {
return String.format("<p>Problem<a style='color:#1A5CC8' href='https://codeforces.com/gym/%s/problem/%s'>%s</a></p><p>" +
"Contest" + ReUtil.get("(<a[^<>]+/gym/\\d+\">.+?</a>)", html, 1)
.replace("/gym", HOST + "/gym")
.replace("color: black", "color: #009688;") + "</p>",
contestNum, problemNum, getJudgeName() + "-" + problemId);
}
@Override
public RemoteProblemInfo getProblemInfo(String problemId, String author) {
try {
return super.getProblemInfo(problemId, author);
} catch (Exception ignored) {
String contestNum = ReUtil.get("([0-9]+)[A-Z]{1}[0-9]{0,1}", problemId, 1);
String problemNum = ReUtil.get("[0-9]+([A-Z]{1}[0-9]{0,1})", problemId, 1);
return getPDFHtml(problemId, contestNum, problemNum, author);
}
}
private RemoteProblemInfo getPDFHtml(String problemId, String contestNum, String problemNum, String author) {
Problem problem = new Problem();
String url = HOST + "/gym/" + contestNum;
String html = HttpUtil.get(url);
String regex = "<a href=\"\\/gym\\/" + contestNum + "\\/problem\\/" + problemNum
+ "\"><!--\\s*-->([^<]+)(?:(?:.|\\s)*?<div){2}[^>]*>\\s*([^<]+)<\\/div>\\s*([\\d.]+)\\D*(\\d+)";
Matcher matcher = Pattern.compile(regex).matcher(html);
matcher.find();
problem.setProblemId(getJudgeName() + "-" + problemId);
problem.setTitle(matcher.group(1));
problem.setTimeLimit((int) (Double.parseDouble(matcher.group(3)) * 1000));
problem.setMemoryLimit(Integer.parseInt(matcher.group(4)));
problem.setSource(String.format("<p>Problem<a style='color:#1A5CC8' href='https://codeforces.com/gym/%s/attachments'>%s</a></p><p>" +
"Contest" + ReUtil.get("(<a[^<>]+/gym/\\d+\">.+?</a>)", html, 1)
.replace("/gym", HOST + "/gym")
.replace("color: black", "color: #009688;") + "</p>",
contestNum, getJudgeName() + "-" + problemId));
regex = "\\/gym\\/" + contestNum + "\\/attachments\\/download\\S*?\\.pdf";
matcher = Pattern.compile(regex).matcher(html);
matcher.find();
String pdfURI;
try {
String fileName = IdUtil.fastSimpleUUID() + ".pdf";
String filePath = Constants.File.PROBLEM_FILE_FOLDER.getPath() + File.separator + fileName;
HttpUtil.downloadFile(IMAGE_HOST + matcher.group(0), filePath);
pdfURI = Constants.File.FILE_API.getPath() + fileName;
} catch (Exception e1) {
pdfURI = HOST + matcher.group(0);
}
String description = "<p><a style='color:#3091f2' href=\"" + pdfURI + "\">Click here to download the PDF file.</a></p>";
problem.setDescription(description);
problem.setType(0)
.setIsRemote(true)
.setAuth(1)
.setAuthor(author)
.setOpenCaseResult(false)
.setIsRemoveEndBlank(false)
.setDifficulty(1); // 默认为中等
return new RemoteProblemInfo().setProblem(problem).setTagList(null);
}
}

View File

@ -82,6 +82,11 @@ public class RemoteJudgeReceiver {
boolean isNeedAccountRejudge = remoteOJName.equals(Constants.RemoteOJ.POJ.getName())
&& isHasSubmitIdRemoteReJudge;
String remoteOJAccountType = remoteOJName; // GYM与CF共用账号
if (remoteOJName.equals(Constants.RemoteOJ.GYM.getName())) {
remoteOJAccountType = Constants.RemoteOJ.CODEFORCES.getName();
}
ToJudge toJudge = new ToJudge();
toJudge.setJudge(judge)
.setTryAgainNum(tryAgainNum)
@ -101,7 +106,7 @@ public class RemoteJudgeReceiver {
QueryWrapper<RemoteJudgeAccount> remoteJudgeAccountQueryWrapper = new QueryWrapper<>();
remoteJudgeAccountQueryWrapper
.eq("status", true)
.eq("oj", remoteOJName)
.eq("oj", remoteOJAccountType)
.last("for update"); // 开启悲观锁
List<RemoteJudgeAccount> remoteJudgeAccountList = remoteJudgeAccountService.list(remoteJudgeAccountQueryWrapper);
@ -165,7 +170,7 @@ public class RemoteJudgeReceiver {
} else {
if (isNeedAccountRejudge) {
if (StringUtils.isEmpty(judge.getVjudgeUsername())){
if (StringUtils.isEmpty(judge.getVjudgeUsername())) {
// poj以往的账号丢失了那么只能重新从头到尾提交
remoteJudgeDispatcher.sendTask(judge, token, remoteJudgeProblem, isContest,
tryAgainNum + 1, false);
@ -173,9 +178,9 @@ public class RemoteJudgeReceiver {
}
QueryWrapper<RemoteJudgeAccount> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("oj", remoteOJName).eq("username", judge.getVjudgeUsername());
queryWrapper.eq("oj", remoteOJAccountType).eq("username", judge.getVjudgeUsername());
int count = remoteJudgeAccountService.count(queryWrapper);
if (count == 0){
if (count == 0) {
// poj以往的账号丢失了那么只能重新从头到尾提交
remoteJudgeDispatcher.sendTask(judge, token, remoteJudgeProblem, isContest,
tryAgainNum + 1, false);

View File

@ -564,6 +564,9 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
case "POJ":
problemStrategy = new POJProblemStrategy();
break;
case "GYM":
problemStrategy = new GYMProblemStrategy();
break;
default:
throw new Exception("未知的OJ的名字暂时不支持");
}
@ -581,7 +584,11 @@ public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> impl
boolean addProblemResult = problemMapper.insert(problem) == 1;
// 为新的其它oj题目添加对应的language
QueryWrapper<Language> languageQueryWrapper = new QueryWrapper<>();
languageQueryWrapper.eq("oj", OJName);
if (OJName.equals("GYM")) {
languageQueryWrapper.eq("oj", "CF");
} else {
languageQueryWrapper.eq("oj", OJName);
}
List<Language> OJLanguageList = languageService.list(languageQueryWrapper);
List<ProblemLanguage> problemLanguageList = new LinkedList<>();
for (Language language : OJLanguageList) {

View File

@ -59,6 +59,7 @@ public class Constants {
public enum RemoteOJ {
HDU("HDU"),
CODEFORCES("CF"),
GYM("GYM"),
POJ("POJ");
private final String name;
@ -165,6 +166,8 @@ public class Constants {
MARKDOWN_FILE_FOLDER("/hoj/file/md"),
PROBLEM_FILE_FOLDER("/hoj/file/problem"),
CONTEST_TEXT_PRINT_FOLDER("/hoj/file/contest_print"),
IMG_API("/api/public/img/"),

View File

@ -74,7 +74,7 @@ public class JudgeController {
HashMap<String, Object> res = new HashMap<>();
res.put("version", "1.6.0");
res.put("version", "1.7.0");
res.put("currentTime", new Date());
res.put("judgeServerName", name);
res.put("cpu", Runtime.getRuntime().availableProcessors());

View File

@ -63,8 +63,12 @@ public class RemoteJudgeToSubmit {
// 将使用的账号放回对应列表
UpdateWrapper<RemoteJudgeAccount> remoteJudgeAccountUpdateWrapper = new UpdateWrapper<>();
remoteJudgeAccountUpdateWrapper.set("status", true)
.eq("oj", remoteJudge)
.eq("username", username);
if (remoteJudge.equals("GYM")) {
remoteJudgeAccountUpdateWrapper.eq("oj", "CF");
} else {
remoteJudgeAccountUpdateWrapper.eq("oj", remoteJudge);
}
boolean isOk = remoteJudgeAccountService.update(remoteJudgeAccountUpdateWrapper);
if (!isOk) {
log.error("远程判题:修正账号为可用状态失败----------->{}", "username:" + username + ",password:" + password);
@ -85,13 +89,17 @@ public class RemoteJudgeToSubmit {
null);
log.error("网络错误---------------->获取不到提交ID");
return;
}else {
} else {
// 对POJ特殊 需要一直保持提交和获取结果时账号唯一所以需要特别过滤
if (!remoteJudge.equals(Constants.RemoteJudge.POJ_JUDGE.getName())) {
UpdateWrapper<RemoteJudgeAccount> remoteJudgeAccountUpdateWrapper = new UpdateWrapper<>();
remoteJudgeAccountUpdateWrapper.set("status", true)
.eq("oj", remoteJudge)
.eq("username", username);
if (remoteJudge.equals("GYM")) {
remoteJudgeAccountUpdateWrapper.eq("oj", "CF");
} else {
remoteJudgeAccountUpdateWrapper.eq("oj", remoteJudge);
}
boolean isOk = remoteJudgeAccountService.update(remoteJudgeAccountUpdateWrapper);
if (!isOk) {
log.error("远程判题:修正账号为可用状态失败----------->{}", "username:" + username + ",password:" + password);

View File

@ -26,8 +26,7 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
public static final String SUBMIT_URL = "problemset/submit";
public static final String SUBMISSION_RESULT_URL = "api/user.status?handle=%s&from=1&count=1000";
public static final String CE_INFO_URL = "data/submitSource";
private List<HttpCookie> cookies = new LinkedList<>();
protected List<HttpCookie> cookies = new LinkedList<>();
private static final Map<String, Constants.Judge> statusMap = new HashMap<String, Constants.Judge>() {{
put("FAILED", Constants.Judge.STATUS_SUBMITTED_FAILED);
@ -204,8 +203,12 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
return MapUtil.builder(new HashMap<String, Object>()).put("status", response.getStatus()).map();
}
protected String getSubmitUrl(String contestNum) {
return IMAGE_HOST + SUBMIT_URL;
}
public void submitCode(String contestId, String problemID, String languageID, String code) {
String csrfToken = getCsrfToken(IMAGE_HOST + SUBMIT_URL);
String csrfToken = getCsrfToken(getSubmitUrl(contestId));
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("csrf_token", csrfToken);
paramMap.put("_tta", 594);
@ -219,7 +222,7 @@ public class CodeForcesJudge implements RemoteJudgeStrategy {
paramMap.put("source", code + getRandomBlankString());
paramMap.put("sourceCodeConfirmed", true);
paramMap.put("doNotShowWarningAgain", "on");
HttpRequest request = HttpUtil.createPost(IMAGE_HOST + SUBMIT_URL + "?csrf_token=" + csrfToken);
HttpRequest request = HttpUtil.createPost(getSubmitUrl(contestId) + "?csrf_token=" + csrfToken);
request.form(paramMap);
request.cookie(this.cookies);
request.header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36");

View File

@ -0,0 +1,14 @@
package top.hcode.hoj.remoteJudge.task.Impl;
/**
* @Author: Himit_ZH
* @Date: 2021/11/6 11:17
* @Description:
*/
public class GYMJudge extends CodeForcesJudge {
@Override
protected String getSubmitUrl(String contestNum) {
return IMAGE_HOST + "gym/" + contestNum + "/submit";
}
}

View File

@ -1,6 +1,7 @@
package top.hcode.hoj.remoteJudge.task;
import top.hcode.hoj.remoteJudge.task.Impl.CodeForcesJudge;
import top.hcode.hoj.remoteJudge.task.Impl.GYMJudge;
import top.hcode.hoj.remoteJudge.task.Impl.HduJudge;
import top.hcode.hoj.remoteJudge.task.Impl.POJJudge;
import top.hcode.hoj.util.Constants;
@ -17,6 +18,8 @@ public class RemoteJudgeFactory {
return new CodeForcesJudge();
case POJ_JUDGE:
return new POJJudge();
case GYM_JUDGE:
return new GYMJudge();
default:
return null;
}

View File

@ -67,6 +67,8 @@ public class Constants {
CF_JUDGE("CF"),
GYM_JUDGE("GYM"),
POJ_JUDGE("POJ"),
HDU_REMOTE_JUDGE_ACCOUNT("Hdu Remote Judge Account"),

View File

@ -153,6 +153,7 @@ export const REMOTE_OJ = [
{name:'HDU',key:"HDU"},
{name:"Codeforces",key:"CF"},
{name:"POJ",key:"POJ"},
{name:"GYM",key:"GYM"},
]
export const CONTEST_STATUS = {

View File

@ -1108,7 +1108,7 @@ export default {
}
}
if (!this.problem.examples.length) {
if (!this.problem.examples.length && !this.problem.isRemote) {
myMessage.error(
this.$i18n.t('m.Problem_Examples') +
' ' +

View File

@ -153,64 +153,73 @@
</div>
<div id="problem-content">
<p class="title">{{ $t('m.Description') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.description"
v-katex
v-highlight
></p>
<p class="title">{{ $t('m.Input') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.input"
v-katex
v-highlight
></p>
<template v-if="problemData.problem.description">
<p class="title">{{ $t('m.Description') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.description"
v-katex
v-highlight
></p>
</template>
<p class="title">{{ $t('m.Output') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.output"
v-katex
v-highlight
></p>
<template v-if="problemData.problem.input">
<p class="title">{{ $t('m.Input') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.input"
v-katex
v-highlight
></p>
</template>
<div
v-for="(example, index) of problemData.problem.examples"
:key="index"
>
<div class="flex-container example">
<div class="example-input">
<p class="title">
{{ $t('m.Sample_Input') }} {{ index + 1 }}
<a
class="copy"
v-clipboard:copy="example.input"
v-clipboard:success="onCopy"
v-clipboard:error="onCopyError"
>
<i class="el-icon-document-copy"></i>
</a>
</p>
<pre>{{ example.input }}</pre>
</div>
<div class="example-output">
<p class="title">
{{ $t('m.Sample_Output') }} {{ index + 1 }}
<a
class="copy"
v-clipboard:copy="example.output"
v-clipboard:success="onCopy"
v-clipboard:error="onCopyError"
>
<i class="el-icon-document-copy"></i>
</a>
</p>
<pre>{{ example.output }}</pre>
<template v-if="problemData.problem.output">
<p class="title">{{ $t('m.Output') }}</p>
<p
class="content markdown-body"
v-html="problemData.problem.output"
v-katex
v-highlight
></p>
</template>
<template v-if="problemData.problem.examples">
<div
v-for="(example, index) of problemData.problem.examples"
:key="index"
>
<div class="flex-container example">
<div class="example-input">
<p class="title">
{{ $t('m.Sample_Input') }} {{ index + 1 }}
<a
class="copy"
v-clipboard:copy="example.input"
v-clipboard:success="onCopy"
v-clipboard:error="onCopyError"
>
<i class="el-icon-document-copy"></i>
</a>
</p>
<pre>{{ example.input }}</pre>
</div>
<div class="example-output">
<p class="title">
{{ $t('m.Sample_Output') }} {{ index + 1 }}
<a
class="copy"
v-clipboard:copy="example.output"
v-clipboard:success="onCopy"
v-clipboard:error="onCopyError"
>
<i class="el-icon-document-copy"></i>
</a>
</p>
<pre>{{ example.output }}</pre>
</div>
</div>
</div>
</div>
</template>
<template v-if="problemData.problem.hint">
<p class="title">{{ $t('m.Hint') }}</p>
@ -1289,7 +1298,10 @@ export default {
}
},
isCFProblem() {
if (this.problemID.indexOf('CF-') == 0) {
if (
this.problemID.indexOf('CF-') == 0 ||
this.problemID.indexOf('GYM-') == 0
) {
return true;
} else {
return false;

View File

@ -143,7 +143,7 @@
<vxe-table-column
field="problemId"
:title="$t('m.Problem_ID')"
width="100"
width="150"
show-overflow
></vxe-table-column>

Binary file not shown.