Accept Merge Request #287 重构后台部分功能;修复 bug : (virusdefender-dev -> master)

Merge Request: 重构后台部分功能;修复 bug
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/287
This commit is contained in:
virusdefender 2015-10-30 15:35:28 +08:00
commit 49ff0d1028
26 changed files with 721 additions and 720 deletions

View File

@ -16,10 +16,11 @@ from rest_framework.views import APIView
from utils.shortcuts import (serializer_invalid_response, error_response, from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, paginate, error_page, paginate_data) success_response, paginate, error_page, paginate_data)
from account.models import SUPER_ADMIN, User from account.models import SUPER_ADMIN, User
from account.decorators import login_required from account.decorators import login_required, super_admin_required
from group.models import Group from group.models import Group
from utils.cache import get_cache_redis from utils.cache import get_cache_redis
from submission.models import Submission from submission.models import Submission
from problem.models import Problem
from .models import (Contest, ContestProblem, CONTEST_ENDED, from .models import (Contest, ContestProblem, CONTEST_ENDED,
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank) CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
@ -90,8 +91,8 @@ class ContestAdminAPIView(APIView):
try: try:
# 超级管理员可以编辑所有的 # 超级管理员可以编辑所有的
contest = Contest.objects.get(id=data["id"]) contest = Contest.objects.get(id=data["id"])
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
contest = contest.get(created_by=request.user) return error_response(u"无权访问!")
except Contest.DoesNotExist: except Contest.DoesNotExist:
return error_response(u"该比赛不存在!") return error_response(u"该比赛不存在!")
try: try:
@ -144,6 +145,18 @@ class ContestAdminAPIView(APIView):
--- ---
response_serializer: ContestSerializer response_serializer: ContestSerializer
""" """
contest_id = request.GET.get("contest_id", None)
if contest_id:
try:
# 普通管理员只能获取自己创建的题目
# 超级管理员可以获取全部的题目
contest = Contest.objects.get(id=contest_id)
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
return error_response(u"题目不存在")
return success_response(ContestSerializer(contest).data)
except Contest.DoesNotExist:
return error_response(u"题目不存在")
if request.user.admin_type == SUPER_ADMIN: if request.user.admin_type == SUPER_ADMIN:
contest = Contest.objects.all().order_by("-create_time") contest = Contest.objects.all().order_by("-create_time")
else: else:
@ -171,8 +184,8 @@ class ContestProblemAdminAPIView(APIView):
data = serializer.data data = serializer.data
try: try:
contest = Contest.objects.get(id=data["contest_id"]) contest = Contest.objects.get(id=data["contest_id"])
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
contest = contest.get(created_by=request.user) return error_response(u"比赛不存在")
except Contest.DoesNotExist: except Contest.DoesNotExist:
return error_response(u"比赛不存在") return error_response(u"比赛不存在")
contest_problem = ContestProblem.objects.create(title=data["title"], contest_problem = ContestProblem.objects.create(title=data["title"],
@ -186,8 +199,7 @@ class ContestProblemAdminAPIView(APIView):
created_by=request.user, created_by=request.user,
hint=data["hint"], hint=data["hint"],
contest=contest, contest=contest,
sort_index=data["sort_index"], sort_index=data["sort_index"])
score=data["score"])
return success_response(ContestProblemSerializer(contest_problem).data) return success_response(ContestProblemSerializer(contest_problem).data)
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
@ -221,7 +233,6 @@ class ContestProblemAdminAPIView(APIView):
contest_problem.hint = data["hint"] contest_problem.hint = data["hint"]
contest_problem.visible = data["visible"] contest_problem.visible = data["visible"]
contest_problem.sort_index = data["sort_index"] contest_problem.sort_index = data["sort_index"]
contest_problem.score = data["score"]
contest_problem.last_update_time = now() contest_problem.last_update_time = now()
contest_problem.save() contest_problem.save()
return success_response(ContestProblemSerializer(contest_problem).data) return success_response(ContestProblemSerializer(contest_problem).data)
@ -238,8 +249,8 @@ class ContestProblemAdminAPIView(APIView):
if contest_problem_id: if contest_problem_id:
try: try:
contest_problem = ContestProblem.objects.get(id=contest_problem_id) contest_problem = ContestProblem.objects.get(id=contest_problem_id)
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN and contest_problem.created_by != request.user:
contest_problem = contest_problem.get(created_by=request.user) return error_response(u"比赛题目不存在")
return success_response(ContestProblemSerializer(contest_problem).data) return success_response(ContestProblemSerializer(contest_problem).data)
except ContestProblem.DoesNotExist: except ContestProblem.DoesNotExist:
return error_response(u"比赛题目不存在") return error_response(u"比赛题目不存在")
@ -261,6 +272,26 @@ class ContestProblemAdminAPIView(APIView):
return paginate(request, contest_problems, ContestProblemSerializer) return paginate(request, contest_problems, ContestProblemSerializer)
class MakeContestProblemPublicAPIView(APIView):
@super_admin_required
def post(self, request):
problem_id = request.data.get("problem_id", -1)
try:
problem = ContestProblem.objects.get(id=problem_id)
except ContestProblem.DoesNotExist:
return error_response(u"比赛不存在")
Problem.objects.create(title=problem.title, description=problem.description,
input_description=problem.input_description,
output_description=problem.output_description,
samples=problem.samples,
test_case_id=problem.test_case_id,
hint=problem.hint, created_by=problem.created_by,
time_limit=problem.time_limit, memory_limit=problem.memory_limit,
visible=False, difficulty=-1, source=problem.contest.title)
return success_response(u"创建成功")
class ContestPasswordVerifyAPIView(APIView): class ContestPasswordVerifyAPIView(APIView):
@login_required @login_required
def post(self, request): def post(self, request):

View File

@ -10,7 +10,7 @@ from judge.judger.result import result
from submission.models import Submission from submission.models import Submission
from problem.models import Problem from problem.models import Problem
from utils.cache import get_cache_redis from utils.cache import get_cache_redis
from contest.models import ContestProblem, Contest, ContestSubmission, CONTEST_UNDERWAY, ContestRank from contest.models import ContestProblem, Contest, CONTEST_UNDERWAY, ContestRank
from account.models import User from account.models import User
logger = logging.getLogger("app_info") logger = logging.getLogger("app_info")

View File

@ -11,7 +11,8 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView, from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView,
ContestPasswordVerifyAPIView, ContestTimeAPIView) ContestPasswordVerifyAPIView, ContestTimeAPIView,
MakeContestProblemPublicAPIView)
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
JoinGroupAPIView, JoinGroupRequestAdminAPIView) JoinGroupAPIView, JoinGroupRequestAdminAPIView)
@ -65,6 +66,7 @@ urlpatterns = [
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"), url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"), url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
url(r'^api/admin/contest_problem/public/', MakeContestProblemPublicAPIView.as_view(), name="make_contest_problem_public"),
url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"),
url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"),
url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(),

View File

@ -136,8 +136,8 @@ class ProblemAdminAPIView(APIView):
# 普通管理员只能获取自己创建的题目 # 普通管理员只能获取自己创建的题目
# 超级管理员可以获取全部的题目 # 超级管理员可以获取全部的题目
problem = Problem.objects.get(id=problem_id) problem = Problem.objects.get(id=problem_id)
if request.user.admin_type != SUPER_ADMIN: if request.user.admin_type != SUPER_ADMIN and problem.created_by != request.user:
problem = problem.get(created_by=request.user) return error_response(u"题目不存在")
return success_response(ProblemSerializer(problem).data) return success_response(ProblemSerializer(problem).data)
except Problem.DoesNotExist: except Problem.DoesNotExist:
return error_response(u"题目不存在") return error_response(u"题目不存在")
@ -248,17 +248,31 @@ class TestCaseUploadAPIView(APIView):
"output_name": str(i + 1) + ".out", "output_name": str(i + 1) + ".out",
"output_md5": md5.hexdigest(), "output_md5": md5.hexdigest(),
"striped_output_md5": striped_md5.hexdigest(), "striped_output_md5": striped_md5.hexdigest(),
"input_size": os.path.getsize(test_case_dir + str(i + 1) + ".in"),
"output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")} "output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")}
# 写入配置文件 # 写入配置文件
with open(test_case_dir + "info", "w") as f: with open(test_case_dir + "info", "w") as f:
f.write(json.dumps(file_info)) f.write(json.dumps(file_info))
return success_response({"test_case_id": problem_test_dir, return success_response({"test_case_id": problem_test_dir,
"file_list": {"input": l[0::2], "file_list": file_info["test_cases"]})
"output": l[1::2]}})
else: else:
return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩") return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩")
def get(self, request):
test_case_id = request.GET.get("test_case_id", None)
if not test_case_id:
return error_response(u"参数错误")
test_case_config = settings.TEST_CASE_DIR + test_case_id + "/info"
try:
f = open(test_case_config)
config = json.loads(f.read())
f.close()
except Exception as e:
return error_response(u"读取测试用例出错")
return success_response({"file_list": config["test_cases"]})
def problem_list_page(request, page=1): def problem_list_page(request, page=1):
""" """

View File

@ -52,10 +52,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
{ name: "首页", { name: "首页",
children: [{name: "主页", hash: "#index/index"}] children: [{name: "主页", hash: "#index/index"}]
}, },
{
name: "通用",
children: [{name: "公告管理", hash: "#announcement/announcement"}]
},
{ {
name: "比赛管理", name: "比赛管理",
children: [{name: "比赛列表", hash: "#contest/contest_list"}, children: [{name: "比赛列表", hash: "#contest/contest_list"},
@ -72,12 +68,14 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
$id: "admin", $id: "admin",
template_url: "template/" + hash + ".html", template_url: "template/" + hash + ".html",
username: "", username: "",
adminType: 1,
groupId: -1, groupId: -1,
problemId: -1, problemId: -1,
adminNavList: [], adminNavList: [],
$contestMode: -1,
$problemId: -1, contestId: -1,
$contestId: -1, contestProblemStatus: "edit",
hide_loading: function () { hide_loading: function () {
$("#loading-gif").hide(); $("#loading-gif").hide();
}, },
@ -94,6 +92,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
success: function(data){ success: function(data){
if(!data.code){ if(!data.code){
vm.username = data.data.username; vm.username = data.data.username;
vm.adminType = data.data.admin_type;
if (data.data.admin_type == 2){ if (data.data.admin_type == 2){
vm.adminNavList = superAdminNav; vm.adminNavList = superAdminNav;
} }
@ -113,24 +112,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
vm.template_url = "template/group/group.html"; vm.template_url = "template/group/group.html";
}); });
vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) {
vm.$problemId = problemId;
vm.$contestId = contestId;
vm.$contestMode = contestMode
vm.template_url = "template/contest/edit_problem.html";
});
vm.$watch("showContestListPage", function () {
vm.template_url = "template/contest/contest_list.html";
});
vm.$watch("showContestSubmissionPage", function (problemId, contestId, contestMode) {
vm.$problemId = problemId;
vm.$contestId = contestId;
vm.$contestMode = contestMode
vm.template_url = "template/contest/submission_list.html";
});
avalon.scan(); avalon.scan();
window.onhashchange = function () { window.onhashchange = function () {

View File

@ -104,6 +104,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return; return;
} }
vm.allGroups = [];
for (var i = 0; i < data.data.length; i++) { for (var i = 0; i < data.data.length; i++) {
var item = data.data[i]; var item = data.data[i];
item["isSelected"] = false; item["isSelected"] = false;

View File

@ -20,6 +20,15 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
search: function () { search: function () {
getPage(1); getPage(1);
avalon.vmodels.contestListPager.currentPage = 1; avalon.vmodels.contestListPager.currentPage = 1;
},
editContest: function(contestId){
avalon.vmodels.admin.contestId = contestId;
avalon.vmodels.admin.template_url = "template/contest/edit_contest.html";
},
showContestProblems: function(contestId){
avalon.vmodels.admin.contestId = contestId;
avalon.vmodels.admin.template_url = "template/contest/problem_list.html";
} }
}) })
} }
@ -42,7 +51,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
vm.contestList = data.data.results; vm.contestList = data.data.results;
vm.announcementList = data.data.results;
avalon.vmodels.contestListPager.totalPage = data.data.total_page; avalon.vmodels.contestListPager.totalPage = data.data.total_page;
} }
else { else {

View File

@ -1,321 +0,0 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () {
$("#edit-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
e.preventDefault();
var ajaxData = {
id: vm.contestList[vm.editingContestId - 1].id,
title: vm.editTitle,
description: vm.editDescription,
mode: vm.editMode,
contest_type: 0,
real_time_rank: vm.editRealTimeRank,
show_user_submission: vm.editShowSubmission,
start_time: vm.editStartTime,
end_time: vm.editEndTime,
visible: vm.editVisible
};
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
}
ajaxData.groups = selectedGroups;
}
else {
if (vm.editPassword) {
ajaxData.password = vm.editPassword;
ajaxData.contest_type = 2;
}
else
ajaxData.contest_type = 1;
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (vm.editDescription == "") {
bsAlert("比赛描述不能为空!");
return false;
}
$.ajax({ // modify contest info
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(ajaxData),
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("修改成功!");
vm.editingContestId = 0; // Hide the editor
vm.getPage(1); // Refresh the contest list
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
if (avalon.vmodels.contestList) {
// this page has been loaded before, so set the default value
var vm = avalon.vmodels.contestList;
vm.contestList = [];
}
else {
var vm = avalon.define({
$id: "contestList",
contestList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
showVisibleOnly: false,
keyword: "",
editingContestId: 0,
editTitle: "",
editDescription: "",
editProblemList: [],
editPassword: "",
editStartTime: "",
editEndTime: "",
editMode: "",
editShowSubmission: false,
editVisible: false,
editRealTimeRank: true,
editingProblemContestIndex: 0,
isGlobal: true,
allGroups: [],
showGlobalViewRadio: true,
admin_type: 1,
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showEditContestArea: function (contestId) {
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?"))
return;
if (contestId == vm.editingContestId)
vm.editingContestId = 0;
else {
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId - 1].title;
vm.editPassword = vm.contestList[contestId - 1].password;
vm.editStartTime = vm.contestList[contestId - 1].start_time.substring(0, 16).replace("T", " ");
vm.editEndTime = vm.contestList[contestId - 1].end_time.substring(0, 16).replace("T", " ");
vm.editMode = vm.contestList[contestId - 1].mode;
vm.editVisible = vm.contestList[contestId - 1].visible;
vm.editRealTimeRank = vm.contestList[contestId - 1].real_time_rank;
if (vm.contestList[contestId - 1].contest_type == 0) { //contest type == 0, contest in group
vm.isGlobal = false;
for (var i = 0; i < vm.allGroups.length; i++) {
vm.allGroups[i].isSelected = false;
}
for (var i = 0; i < vm.contestList[contestId - 1].groups.length; i++) {
var id = parseInt(vm.contestList[contestId - 1].groups[i]);
for (var index = 0; vm.allGroups[index]; index++) {
if (vm.allGroups[index].id == id) {
vm.allGroups[index].isSelected = true;
break;
}
}
}
}
else {
vm.isGlobal = true;
}
vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission;
editor("#editor").setValue(vm.contestList[contestId - 1].description);
vm.editingProblemContestIndex = 0;
}
},
showEditProblemArea: function (contestId) {
if (vm.editingProblemContestIndex == contestId) {
vm.editingProblemContestIndex = 0;
return;
}
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) {
return;
}
$.ajax({ // Get the problem list of current contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId - 1].id,
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
vm.editProblemList = data.data;
}
else {
bsAlert(data.data);
}
}
});
vm.editingContestId = 0;
vm.editingProblemContestIndex = contestId;
vm.editMode = vm.contestList[contestId - 1].mode;
},
addProblem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showProblemEditPage: function (el) {
vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showSubmissionPage: function (el) {
var problemId = 0
if (el)
problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
addToProblemList: function (problem) {
var ajaxData = {
title: problem.title,
description: problem.description,
time_limit: problem.time_limit,
memory_limit: problem.memory_limit,
samples: [],
test_case_id: problem.test_case_id,
hint: problem.hint,
source: problem.contest.title,
visible: false,
tags: [],
input_description: problem.input_description,
output_description: problem.output_description,
difficulty: 0
};
for (var i = 0; i < problem.samples.length; i++) {
ajaxData.samples.push({input: problem.samples[i].input, output: problem.samples[i].output})
}
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/problem/",
dataType: "json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json;charset=UTF-8",
success: function (data) {
if (!data.code) {
bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");
}
else {
bsAlert(data.data);
}
}
});
}
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
})
}
getPageData(1);
//init time picker
$("#contest_start_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
$("#contest_end_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
function getPageData(page) {
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10";
if (vm.showVisibleOnly)
url += "&visible=true"
if (vm.keyword != "")
url += "&keyword=" + vm.keyword;
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.contestList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
// Get group list
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
var admin_type = data.data.admin_type;
vm.admin_type = admin_type;
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["isSelected"] = false;
vm.allGroups.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
});
});
avalon.scan();
});

View File

@ -0,0 +1,64 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () {
if (avalon.vmodels.contestProblemList) {
vm = avalon.vmodels.contestProblemList;
}
else {
var vm = avalon.define({
$id: "contestProblemList",
problemList: [],
adminType: avalon.vmodels.admin.adminType,
showEditProblemPage: function (problemId) {
avalon.vmodels.admin.contestProblemStatus = "edit";
avalon.vmodels.admin.problemId = problemId;
avalon.vmodels.admin.template_url = "template/contest/edit_problem.html";
},
addProblem: function(){
avalon.vmodels.admin.contestProblemStatus = "add";
avalon.vmodels.admin.template_url = "template/contest/edit_problem.html";
},
goBack: function(){
avalon.vmodels.admin.template_url = "template/contest/contest_list.html"
},
makeProblemPublic: function(problem){
$.ajax({
url: "/api/admin/contest_problem/public/",
method: "post",
dataType: "json",
data: {"problem_id": problem.id},
success: function(response){
if(response.code){
bsAlert(response.data);
}
else{
problem.is_public = true;
alert("公开题目成功,现在处于隐藏状态,请添加标签难度等信息。");
}
}
})
}
});
}
$.ajax({
url: "/api/admin/contest_problem/?contest_id=" + avalon.vmodels.admin.contestId,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.problemList = data.data;
}
else {
bsAlert(data.data);
}
}
});
avalon.scan();
});
});

View File

@ -0,0 +1,175 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
"validator", "editorComponent"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
$("#edit-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
e.preventDefault();
var ajaxData = {
id: avalon.vmodels.admin.contestId,
title: vm.title,
description: avalon.vmodels.contestDescriptionEditor.content,
contest_type: 0,
real_time_rank: vm.realTimeRank,
start_time: vm.startTime,
end_time: vm.endTime,
visible: vm.visible
};
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
}
ajaxData.groups = selectedGroups;
}
else {
if (vm.password) {
ajaxData.password = vm.password;
ajaxData.contest_type = 2;
}
else
ajaxData.contest_type = 1;
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (ajaxData.description.trim() == "") {
bsAlert("比赛描述不能为空!");
return false;
}
$.ajax({
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(ajaxData),
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("修改成功!");
vm.showContestListPage();
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
if (avalon.vmodels.edit_contest)
var vm = avalon.vmodels.edit_contest;
else
var vm = avalon.define({
$id: "edit_contest",
title: "",
startTime: "",
endTime: "",
password: "",
isGlobal: true,
allGroups: [],
showGlobalViewRadio: true,
realTimeRank: true,
visible: false,
showContestListPage: function () {
avalon.vmodels.admin.template_url = "template/contest/contest_list.html";
},
contestDescriptionEditor: {
editorId: "contest-description-editor",
placeholder: "比赛介绍内容"
}
});
avalon.scan();
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
var admin_type = data.data.admin_type;
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return;
}
vm.allGroups = [];
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item.isSelected = false;
vm.allGroups.push(item);
}
$.ajax({
url: "/api/admin/contest/?contest_id=" + avalon.vmodels.admin.contestId,
method: "get",
dataType: "json",
success: function (data) {
if (data.code) {
bsAlert(data.data);
}
else {
var contest = data.data;
vm.title = contest.title;
avalon.vmodels.contestDescriptionEditor.content = contest.description;
vm.visible = contest.visible;
vm.realTimeRank = contest.real_time_rank;
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
if (contest.contest_type == 0) { //contest_type == 0, 小组内比赛
vm.isGlobal = false;
for (var i = 0; i < vm.allGroups.length; i++) {
vm.allGroups[i].isSelected = false;
}
for (var i = 0; i < contest.groups.length; i++) {
var id = contest.groups[i];
for (var index = 0; vm.allGroups[index]; index++) {
if (vm.allGroups[index].id == id) {
vm.allGroups[index].isSelected = true;
break;
}
}
}
}
else {
vm.isGlobal = true;
}
}
}
});
}
else {
bsAlert(data.data);
}
}
});
}
});
$("#contest_start_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
$("#contest_end_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
});

View File

@ -1,22 +1,22 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "editorComponent"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () { avalon.ready(function () {
$("#edit-problem-form").validator() $("#edit-problem-form").validator()
.on('submit', function (e) { .on('submit', function (e) {
if (!e.isDefaultPrevented()){ if (!e.isDefaultPrevented()) {
e.preventDefault(); e.preventDefault();
if (vm.testCaseId == "") { if (vm.testCaseId == "") {
bsAlert("你还没有上传测试数据!"); bsAlert("你还没有上传测试数据!");
return false; return false;
} }
if (vm.description == "") { if (avalon.vmodels.contestProblemDescriptionEditor.content == "") {
bsAlert("题目描述不能为空!"); bsAlert("题目描述不能为空!");
return false; return false;
} }
if (vm.timeLimit < 1000 || vm.timeLimit > 5000) { if (vm.timeLimit < 30 || vm.timeLimit > 5000) {
bsAlert("保证时间限制是一个1000-5000的合法整数"); bsAlert("保证时间限制是一个30-5000的合法整数");
return false; return false;
} }
if (vm.samples.length == 0) { if (vm.samples.length == 0) {
@ -31,33 +31,34 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
} }
var ajaxData = { var ajaxData = {
title: vm.title, title: vm.title,
description: vm.description, description: avalon.vmodels.contestProblemDescriptionEditor.content,
time_limit: vm.timeLimit, time_limit: vm.timeLimit,
memory_limit: vm.memoryLimit, memory_limit: vm.memoryLimit,
samples: [], samples: [],
test_case_id: vm.testCaseId, test_case_id: vm.testCaseId,
hint: vm.hint, hint: avalon.vmodels.contestProblemHintEditor.content,
visible: vm.visible, visible: vm.visible,
contest_id: avalon.vmodels.admin.$contestId, contest_id: avalon.vmodels.admin.contestId,
input_description: vm.inputDescription, input_description: vm.inputDescription,
output_description: vm.outputDescription, output_description: vm.outputDescription,
sort_index: vm.sortIndex, sort_index: vm.sortIndex
}; };
if (vm.contestMode == '2') {
if (!vm.score) { if (avalon.vmodels.admin.contestProblemStatus == "edit") {
bsAlert("请输入有效的分值!") var method = "put";
return false; ajaxData["id"] = avalon.vmodels.admin.problemId;
} var alertContent = "题目编辑成功";
ajaxData.score = vm.score;
} }
var method = "post"; else{
if (avalon.vmodels.admin.$problemId) { var method = "post";
method = "put"; var alertContent = "题目创建成功";
ajaxData.id = avalon.vmodels.admin.$problemId;
} }
for (var i = 0; i < vm.samples.$model.length; i++) { for (var i = 0; i < vm.samples.$model.length; i++) {
ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output}); ajaxData.samples.push({
input: vm.samples.$model[i].input,
output: vm.samples.$model[i].output
});
} }
$.ajax({ $.ajax({
@ -69,8 +70,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
contentType: "application/json;charset=UTF-8", contentType: "application/json;charset=UTF-8",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
bsAlert("题目编辑成功!"); bsAlert(alertContent);
vm.goBack(true);
} }
else { else {
bsAlert(data.data); bsAlert(data.data);
@ -82,93 +82,101 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
} }
}); });
if (!avalon.vmodels.editProblem) if (!avalon.vmodels.editProblem)
var vm = avalon.define({ var vm = avalon.define({
$id: "editProblem", $id: "editProblem",
title: "", title: "",
description: "", description: "",
timeLimit: 0, timeLimit: 1000,
memoryLimit: 0, memoryLimit: 128,
samples: [], samples: [],
hint: "", hint: "",
sortIndex: "", sortIndex: "",
visible: true, visible: true,
inputDescription: "", inputDescription: "",
outputDescription: "", outputDescription: "",
testCaseIdd: "", testCaseId: "",
contestMode: 0, testCaseList: [],
score: 1, uploadSuccess: false,
uploadSuccess: false,
testCaseList: [], contestProblemDescriptionEditor: {
addSample: function () { editorId: "contest-problem-description-editor",
vm.samples.push({input: "", output: "", "visible": true}); placeholder: "题目描述"
}, },
delSample: function (sample) { contestProblemHintEditor: {
if (confirm("你确定要删除么?")) { editorId: "contest-problem-hint-editor",
vm.samples.remove(sample); placeholder: "提示"
} },
},
toggleSample: function (sample) { addSample: function () {
sample.visible = !sample.visible; vm.samples.push({input: "", output: "", "visible": true});
}, },
getBtnContent: function (item) {
if (item.visible) delSample: function (sample) {
return "折叠"; if (confirm("你确定要删除么?")) {
return "展开"; vm.samples.remove(sample);
}, }
goBack: function(check){ },
if (check||confirm("这将丢失所有的改动,确定要继续么?")) {
vm.$fire("up!showContestListPage"); toggleSample: function (sample) {
} sample.visible = !sample.visible;
} },
});
else getBtnContent: function (item) {
vm = avalon.vmodels.editProblem; if (item.visible)
return "折叠";
return "展开";
},
goBack: function (check) {
avalon.vmodels.admin.template_url = "template/contest/problem_list.html";
}
});
else {
var vm = avalon.vmodels.editProblem;
}
var hintEditor = editor("#hint");
var descriptionEditor = editor("#problemDescription");
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) {
if (response.code) if (response.code)
bsAlert(response.data); bsAlert(response.data);
else { else {
vm.testCaseId = response.data.test_case_id; vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true;
vm.testCaseList = []; vm.testCaseList = [];
for (var i = 0; i < response.data.file_list.input.length; i++) { for(var key in response.data.file_list){
vm.testCaseList.push({ vm.testCaseList.push({
input: response.data.file_list.input[i], input: response.data.file_list[key].input_name,
output: response.data.file_list.output[i] output: response.data.file_list[key].output_name
}); })
} }
vm.uploadSuccess = true;
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
} }
}); });
vm.contestMode = avalon.vmodels.admin.$contestMode; if (avalon.vmodels.admin.contestProblemStatus == "edit") {
if (avalon.vmodels.admin.$problemId){
$.ajax({ $.ajax({
url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.$problemId, url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.problemId,
method: "get", method: "get",
dataType: "json", dataType: "json",
success: function (data) { success: function (data) {
if (data.code) { if (data.code) {
bsAlert(data.data); bsAlert(data.data);
} }
else { // Edit mode load the problem data else {
var problem = data.data; var problem = data.data;
vm.testCaseList = []; vm.testCaseList = [];
vm.sortIndex = problem.sort_index; vm.sortIndex = problem.sort_index;
vm.title = problem.title; vm.title = problem.title;
vm.description = problem.description; avalon.vmodels.contestProblemDescriptionEditor.content = problem.description;
vm.timeLimit = problem.time_limit; vm.timeLimit = problem.time_limit;
vm.memoryLimit = problem.memory_limit; vm.memoryLimit = problem.memory_limit;
vm.hint = problem.hint; vm.hint = problem.hint;
vm.visible = problem.visible; vm.visible = problem.visible;
vm.inputDescription = problem.input_description; vm.inputDescription = problem.input_description;
vm.outputDescription = problem.output_description; vm.outputDescription = problem.output_description;
vm.score = problem.score; vm.score = problem.score;
vm.samples = []; vm.testCaseId = problem.test_case_id;
vm.testCaseId = problem.test_case_id; vm.samples = [];
for (var i = 0; i < problem.samples.length; i++) { for (var i = 0; i < problem.samples.length; i++) {
vm.samples.push({ vm.samples.push({
input: problem.samples[i].input, input: problem.samples[i].input,
@ -176,26 +184,32 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
visible: false visible: false
}) })
} }
hintEditor.setValue(vm.hint); avalon.vmodels.contestProblemHintEditor.content = problem.hint;
descriptionEditor.setValue(vm.description); $.ajax({
url: "/api/admin/test_case_upload/?test_case_id=" + vm.testCaseId,
method: "get",
dataType: "json",
success: function(response){
if(response.code){
bsAlert(response.data);
}
else {
vm.testCaseList = [];
for (var key in response.data.file_list) {
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
vm.uploadSuccess = true;
}
}
})
} }
} }
}); });
}
else { //Create new problem Set default values
vm.testCaseList = [];
vm.title = "";
vm.timeLimit = 1000;
vm.memoryLimit = 256;
vm.samples = [];
vm.visible = true;
vm.inputDescription = "";
vm.outputDescription = "";
vm.testCaseId = "";
vm.sortIndex = "";
vm.score = 0;
hintEditor.setValue("");
descriptionEditor.setValue("");
} }
}); });
avalon.scan(); avalon.scan();

View File

@ -1,88 +0,0 @@
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
avalon.ready(function () {
if (avalon.vmodels.contestSubmissionList){
var vm = avalon.vmodels.contestSubmissionList;
}
else {
var vm = avalon.define({
$id: "contestSubmissionList",
submissionList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
results : {
0: "Accepted",
1: "Runtime Error",
2: "Time Limit Exceeded",
3: "Memory Limit Exceeded",
4: "Compile Error",
5: "Format Error",
6: "Wrong Answer",
7: "System Error",
8: "Waiting"
},
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
if (!page_index)
var page_index = vm.page;
getPageData(page_index);
},
showSubmissionDetailPage: function (submissionId) {
},
goBack: function(check){
vm.$fire("up!showContestListPage");
}
});
}
getPageData(1);
function getPageData(page) {
var url = "/api/admin/contest_submission/?paging=true&page=" + page + "&page_size=10&contest_id=" + avalon.vmodels.admin.$contestId;
if (avalon.vmodels.admin.$problemId)
url += "&problem_id=" + avalon.vmodels.admin.$problemId
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.submissionList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
});
avalon.scan();
});

View File

@ -135,17 +135,17 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
vm.testCaseId = response.data.test_case_id; vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true; vm.uploadSuccess = true;
vm.testCaseList = []; vm.testCaseList = [];
for (var i = 0; i < response.data.file_list.input.length; i++) { for(var key in response.data.file_list){
vm.testCaseList.push({ vm.testCaseList.push({
input: response.data.file_list.input[i], input: response.data.file_list[key].input_name,
output: response.data.file_list.output[i] output: response.data.file_list[key].output_name
}); })
} }
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
} }
}, },
function (file, percentage) { function (file, percentage) {
vm.uploadProgress = percentage; vm.uploadProgress = parseInt(percentage * 100);
}); });
var tagAutoCompleteList = []; var tagAutoCompleteList = [];

View File

@ -137,17 +137,17 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
vm.testCaseId = response.data.test_case_id; vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true; vm.uploadSuccess = true;
vm.testCaseList = []; vm.testCaseList = [];
for (var i = 0; i < response.data.file_list.input.length; i++) { for(var key in response.data.file_list){
vm.testCaseList.push({ vm.testCaseList.push({
input: response.data.file_list.input[i], input: response.data.file_list[key].input_name,
output: response.data.file_list.output[i] output: response.data.file_list[key].output_name
}); })
} }
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
} }
}, },
function (file, percentage) { function (file, percentage) {
vm.uploadProgress = percentage; vm.uploadProgress = parseInt(percentage * 100);
} }
); );

View File

@ -62,17 +62,18 @@
admin_8_pack: "app/admin/admin", admin_8_pack: "app/admin/admin",
login_9_pack: "app/oj/account/login", login_9_pack: "app/oj/account/login",
addContest_10_pack: "app/admin/contest/addContest", addContest_10_pack: "app/admin/contest/addContest",
changePassword_11_pack: "app/oj/account/changePassword", contestPassword_11_pack: "app/oj/contest/contestPassword",
monitor_12_pack: "app/admin/monitor/monitor", changePassword_12_pack: "app/oj/account/changePassword",
editProblem_13_pack: "app/admin/contest/editProblem", monitor_13_pack: "app/admin/monitor/monitor",
joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList", editProblem_14_pack: "app/admin/contest/editProblem",
group_15_pack: "app/oj/group/group", joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList",
editProblem_16_pack: "app/admin/problem/editProblem", group_16_pack: "app/oj/group/group",
register_17_pack: "app/oj/account/register", contestProblemList_17_pack: "app/admin/contest/contestProblemList",
groupDetail_18_pack: "app/admin/group/groupDetail", editProblem_18_pack: "app/admin/problem/editProblem",
contestPassword_19_pack: "app/oj/contest/contestPassword", register_19_pack: "app/oj/account/register",
group_20_pack: "app/admin/group/group", groupDetail_20_pack: "app/admin/group/groupDetail",
submissionList_21_pack: "app/admin/contest/submissionList" editContest_21_pack: "app/admin/contest/editContest",
group_22_pack: "app/admin/group/group",
}, },
shim: { shim: {
avalon: { avalon: {
@ -123,38 +124,41 @@
name: "addContest_10_pack" name: "addContest_10_pack"
}, },
{ {
name: "changePassword_11_pack" name: "contestPassword_11_pack"
}, },
{ {
name: "monitor_12_pack" name: "changePassword_12_pack"
}, },
{ {
name: "editProblem_13_pack" name: "monitor_13_pack"
}, },
{ {
name: "joinGroupRequestList_14_pack" name: "editProblem_14_pack"
}, },
{ {
name: "group_15_pack" name: "joinGroupRequestList_15_pack"
}, },
{ {
name: "editProblem_16_pack" name: "group_16_pack"
}, },
{ {
name: "register_17_pack" name: "contestProblemList_17_pack"
}, },
{ {
name: "groupDetail_18_pack" name: "editProblem_18_pack"
}, },
{ {
name: "contestPassword_19_pack" name: "register_19_pack"
}, },
{ {
name: "group_20_pack" name: "groupDetail_20_pack"
}, },
{ {
name: "submissionList_21_pack" name: "editContest_21_pack"
}, },
{
name: "group_22_pack"
}
], ],
optimizeCss: "standard", optimizeCss: "standard",
}) })

View File

@ -64,17 +64,18 @@ var require = {
admin_8_pack: "app/admin/admin", admin_8_pack: "app/admin/admin",
login_9_pack: "app/oj/account/login", login_9_pack: "app/oj/account/login",
addContest_10_pack: "app/admin/contest/addContest", addContest_10_pack: "app/admin/contest/addContest",
changePassword_11_pack: "app/oj/account/changePassword", contestPassword_11_pack: "app/oj/contest/contestPassword",
monitor_12_pack: "app/admin/monitor/monitor", changePassword_12_pack: "app/oj/account/changePassword",
editProblem_13_pack: "app/admin/contest/editProblem", monitor_13_pack: "app/admin/monitor/monitor",
joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList", editProblem_14_pack: "app/admin/contest/editProblem",
group_15_pack: "app/oj/group/group", joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList",
editProblem_16_pack: "app/admin/problem/editProblem", group_16_pack: "app/oj/group/group",
register_17_pack: "app/oj/account/register", contestProblemList_17_pack: "app/admin/contest/contestProblemList",
groupDetail_18_pack: "app/admin/group/groupDetail", editProblem_18_pack: "app/admin/problem/editProblem",
contestPassword_19_pack: "app/oj/contest/contestPassword", register_19_pack: "app/oj/account/register",
group_20_pack: "app/admin/group/group", groupDetail_20_pack: "app/admin/group/groupDetail",
submissionList_21_pack: "app/admin/contest/submissionList" editContest_21_pack: "app/admin/contest/editContest",
group_22_pack: "app/admin/group/group",
}, },
shim: { shim: {
avalon: { avalon: {

View File

@ -5,7 +5,7 @@
http://weibo.com/jslouvre/ http://weibo.com/jslouvre/
Released under the MIT license Released under the MIT license
avalon.shim.js 1.5.4 built in 2015.10.18 avalon.shim.js 1.5.5 built in 2015.10.27
support IE6+ and other browsers support IE6+ and other browsers
==================================================*/ ==================================================*/
(function(global, factory) { (function(global, factory) {
@ -285,7 +285,7 @@ function _number(a, len) { //用于模拟slice, splice的效果
avalon.mix({ avalon.mix({
rword: rword, rword: rword,
subscribers: subscribers, subscribers: subscribers,
version: 1.54, version: 1.55,
ui: {}, ui: {},
log: log, log: log,
slice: W3C ? function (nodes, start, end) { slice: W3C ? function (nodes, start, end) {
@ -940,6 +940,7 @@ kernel.maxRepeatSize = 100
avalon.config = kernel avalon.config = kernel
function $watch(expr, binding) { function $watch(expr, binding) {
var $events = this.$events || (this.$events = {}) var $events = this.$events || (this.$events = {})
var queue = $events[expr] || ($events[expr] = []) var queue = $events[expr] || ($events[expr] = [])
if (typeof binding === "function") { if (typeof binding === "function") {
var backup = binding var backup = binding
@ -981,31 +982,41 @@ function $watch(expr, binding) {
binding.element = DOC.createElement("a") binding.element = DOC.createElement("a")
} }
} }
function $emit(key, args) { function $emit(key, args) {
var event = this.$events var event = this.$events
if (event && event[key]) { if (event && event[key]) {
if (args) { if (args) {
args[2] = key args[2] = key
} }
notifySubscribers(event[key], args) var arr = event[key]
notifySubscribers(arr, args)
var parent = this.$up var parent = this.$up
if (parent) { if (parent) {
if (this.$pathname) { if (this.$pathname) {
$emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡 $emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡
} }
$emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡
} }
} else { } else {
parent = this.$up parent = this.$up
if(this.$ups ){
for(var i in this.$ups){
$emit.call(this.$ups[i], i+"."+key, args)//以确切的值往上冒泡
}
return
}
if (parent) { if (parent) {
var path = this.$pathname + "." + key var p = this.$pathname
var arr = path.split(".") if (p === "")
p = "*"
var path = p + "." + key
arr = path.split(".")
if (arr.indexOf("*") === -1) { if (arr.indexOf("*") === -1) {
$emit.call(parent, path, args)//以确切的值往上冒泡 $emit.call(parent, path, args)//以确切的值往上冒泡
arr[1] = "*" arr[1] = "*"
$emit.call(parent, arr.join("."), args)//以确切的值往上冒泡 $emit.call(parent, arr.join("."), args)//以模糊的值往上冒泡
} else { } else {
$emit.call(parent, path, args)//以确切的值往上冒泡 $emit.call(parent, path, args)//以确切的值往上冒泡
} }
@ -1086,7 +1097,7 @@ avalon.define = function (source) {
} }
//一些不需要被监听的属性 //一些不需要被监听的属性
var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors") var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors,$ups")
var defineProperty = Object.defineProperty var defineProperty = Object.defineProperty
var canHideOwn = true var canHideOwn = true
//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG比如IE8 //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG比如IE8
@ -1212,6 +1223,7 @@ function observeObject(source, options) {
}) })
/* jshint ignore:start */ /* jshint ignore:start */
hideProperty($vmodel, "$ups", null)
hideProperty($vmodel, "$id", "anonymous") hideProperty($vmodel, "$id", "anonymous")
hideProperty($vmodel, "$up", old ? old.$up : null) hideProperty($vmodel, "$up", old ? old.$up : null)
hideProperty($vmodel, "$track", Object.keys(hasOwn)) hideProperty($vmodel, "$track", Object.keys(hasOwn))
@ -1236,15 +1248,15 @@ function observeObject(source, options) {
}) })
} }
/* jshint ignore:end */ /* jshint ignore:end */
//必须设置了$active,$events //必须设置了$active,$events
simple.forEach(function (name) { simple.forEach(function (name) {
var oldVal = old && old[name]
var val = $vmodel[name] = source[name] var val = $vmodel[name] = source[name]
if (val && typeof val === "object") { if (val && typeof val === "object") {
val.$up = $vmodel val.$up = $vmodel
val.$pathname = name val.$pathname = name
} }
$emit.call($vmodel, name) $emit.call($vmodel, name, [val,oldVal])
}) })
for (name in computed) { for (name in computed) {
value = $vmodel[name] value = $vmodel[name]
@ -1316,7 +1328,7 @@ function observe(obj, old, hasReturn, watch) {
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return observeArray(obj, old, watch) return observeArray(obj, old, watch)
} else if (avalon.isPlainObject(obj)) { } else if (avalon.isPlainObject(obj)) {
if (old) { if (old && typeof old === 'object') {
var keys = getKeys(obj) var keys = getKeys(obj)
var keys2 = getKeys(old) var keys2 = getKeys(old)
if (keys.join(";") === keys2.join(";")) { if (keys.join(";") === keys2.join(";")) {
@ -1356,7 +1368,6 @@ function observeArray(array, old, watch) {
for (var i in newProto) { for (var i in newProto) {
array[i] = newProto[i] array[i] = newProto[i]
} }
hideProperty(array, "$up", null) hideProperty(array, "$up", null)
hideProperty(array, "$pathname", "") hideProperty(array, "$pathname", "")
hideProperty(array, "$track", createTrack(array.length)) hideProperty(array, "$track", createTrack(array.length))
@ -2862,7 +2873,7 @@ function parseExpr(expr, vmodels, binding) {
} }
//======== //========
function stringifyExpr(code) { function normalizeExpr(code) {
var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况 var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况
if (hasExpr) { if (hasExpr) {
var array = scanExpr(code) var array = scanExpr(code)
@ -2877,6 +2888,7 @@ function stringifyExpr(code) {
} }
} }
avalon.normalizeExpr = normalizeExpr
avalon.parseExprProxy = parseExpr avalon.parseExprProxy = parseExpr
var rthimRightParentheses = /\)\s*$/ var rthimRightParentheses = /\)\s*$/
@ -3409,7 +3421,7 @@ avalon.component = function (name, opts) {
componentDefinition.$id = $id componentDefinition.$id = $id
//==========构建VM========= //==========构建VM=========
var keepSolt = componentDefinition.$slot var keepSlot = componentDefinition.$slot
var keepReplace = componentDefinition.$replace var keepReplace = componentDefinition.$replace
var keepContainer = componentDefinition.$container var keepContainer = componentDefinition.$container
var keepTemplate = componentDefinition.$template var keepTemplate = componentDefinition.$template
@ -3422,13 +3434,11 @@ avalon.component = function (name, opts) {
elem.msResolved = 1 elem.msResolved = 1
vmodel.$init(vmodel, elem) vmodel.$init(vmodel, elem)
global.$init(vmodel, elem) global.$init(vmodel, elem)
console.log("init")
var nodes = elem.childNodes var nodes = elem.childNodes
//收集插入点 //收集插入点
var slots = {}, snode var slots = {}, snode
for (var s = 0, el; el = nodes[s++]; ) { for (var s = 0, el; el = nodes[s++]; ) {
var type = el.nodeType === 1 && el.getAttribute("slot") || keepSolt var type = el.nodeType === 1 && el.getAttribute("slot") || keepSlot
if (type) { if (type) {
if (slots[type]) { if (slots[type]) {
slots[type].push(el) slots[type].push(el)
@ -3482,7 +3492,6 @@ avalon.component = function (name, opts) {
e.stopPropagation() e.stopPropagation()
} }
} }
console.log("dependencies "+dependencies)
if (dependencies === 0) { if (dependencies === 0) {
var id1 = setTimeout(function () { var id1 = setTimeout(function () {
clearTimeout(id1) clearTimeout(id1)
@ -3512,7 +3521,6 @@ avalon.component = function (name, opts) {
scanTag(elem, [vmodel].concat(host.vmodels)) scanTag(elem, [vmodel].concat(host.vmodels))
avalon.vmodels[vmodel.$id] = vmodel avalon.vmodels[vmodel.$id] = vmodel
avalon.log("添加组件VM: "+vmodel.$id)
if (!elem.childNodes.length) { if (!elem.childNodes.length) {
avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1})
} else { } else {
@ -3627,7 +3635,7 @@ var attrDir = avalon.directive("attr", {
init: function (binding) { init: function (binding) {
//{{aaa}} --> aaa //{{aaa}} --> aaa
//{{aaa}}/bbb.html --> (aaa) + "/bbb.html" //{{aaa}}/bbb.html --> (aaa) + "/bbb.html"
binding.expr = stringifyExpr(binding.expr.trim()) binding.expr = normalizeExpr(binding.expr.trim())
if (binding.type === "include") { if (binding.type === "include") {
var elem = binding.element var elem = binding.element
effectBinding(elem, binding) effectBinding(elem, binding)
@ -3865,7 +3873,7 @@ var duplexBinding = avalon.directive("duplex", {
} }
var updateVModel = function () { var updateVModel = function () {
var val = elem.value //防止递归调用形成死循环 var val = elem.value //防止递归调用形成死循环
if (composing || val === binding.oldValue) //处理中文输入法在minlengh下引发的BUG if (composing || val === binding.oldValue || binding.pipe === null) //处理中文输入法在minlengh下引发的BUG
return return
var lastValue = binding.pipe(val, binding, "get") var lastValue = binding.pipe(val, binding, "get")
try { try {
@ -3955,7 +3963,7 @@ var duplexBinding = avalon.directive("duplex", {
var curValue = Array.isArray(value) ? value.map(String) : value + "" var curValue = Array.isArray(value) ? value.map(String) : value + ""
avalon(elem).val(curValue) avalon(elem).val(curValue)
elem.oldValue = curValue + "" elem.oldValue = curValue + ""
binding.changed.call(elem, curValue) callback.call(elem, curValue)
} }
}) })
break break
@ -4000,15 +4008,18 @@ var duplexBinding = avalon.directive("duplex", {
if (curValue !== this.oldValue) { if (curValue !== this.oldValue) {
var fixCaret = false var fixCaret = false
if (elem.msFocus) { if (elem.msFocus) {
var pos = getCaret(elem) try {
if (pos.start === pos.end) { var pos = getCaret(elem)
pos = pos.start if (pos.start === pos.end) {
fixCaret = true pos = pos.start
fixCaret = true
}
} catch (e) {
} }
} }
elem.value = this.oldValue = curValue elem.value = this.oldValue = curValue
if (fixCaret) { if (fixCaret) {
setCaret(element, pos, pos) setCaret(elem, pos, pos)
} }
} }
break break
@ -4034,10 +4045,10 @@ var duplexBinding = avalon.directive("duplex", {
case "select": case "select":
//必须变成字符串后才能比较 //必须变成字符串后才能比较
binding._value = value binding._value = value
if(!elem.msHasEvent){ if (!elem.msHasEvent) {
elem.msHasEvent = "selectDuplex" elem.msHasEvent = "selectDuplex"
//必须等到其孩子准备好才触发 //必须等到其孩子准备好才触发
}else{ } else {
avalon.fireDom(elem, "datasetchanged", { avalon.fireDom(elem, "datasetchanged", {
bubble: elem.msHasEvent bubble: elem.msHasEvent
}) })
@ -4045,7 +4056,7 @@ var duplexBinding = avalon.directive("duplex", {
break break
} }
if (binding.xtype !== "select") { if (binding.xtype !== "select") {
binding.changed.call(elem, curValue) binding.changed.call(elem, curValue, binding)
} }
} }
}) })
@ -4178,18 +4189,19 @@ function getCaret(ctrl, start, end) {
function setCaret(ctrl, begin, end) { function setCaret(ctrl, begin, end) {
if (!ctrl.value || ctrl.readOnly) if (!ctrl.value || ctrl.readOnly)
return return
if (ctrl.setSelectionRange) { if (ctrl.createTextRange) {//IE6-9
setTimeout(function () {
var range = ctrl.createTextRange()
range.collapse(true);
range.moveStart("character", begin)
// range.moveEnd("character", end) #1125
range.select()
}, 17)
} else {
ctrl.selectionStart = begin ctrl.selectionStart = begin
ctrl.selectionEnd = end ctrl.selectionEnd = end
} else {
var range = ctrl.createTextRange()
range.collapse(true);
range.moveStart("character", begin)
range.moveEnd("character", end - begin)
range.select()
} }
} }
avalon.directive("effect", { avalon.directive("effect", {
priority: 5, priority: 5,
init: function (binding) { init: function (binding) {
@ -4209,7 +4221,7 @@ avalon.directive("effect", {
if (!rexpr.test(text)) { if (!rexpr.test(text)) {
className = quote(className) className = quote(className)
} else { } else {
className = stringifyExpr(className) className = normalizeExpr(className)
} }
binding.expr = "[" + className + "," + rightExpr + "]" binding.expr = "[" + className + "," + rightExpr + "]"
}, },
@ -4372,7 +4384,7 @@ function upperFirstChar(str) {
} }
var effectBuffer = new Buffer() var effectBuffer = new Buffer()
function Effect() { function Effect() {
}// 动画实例,做成类的形式,是为了共用所有原型方法 }// 动画实例,做成类的形式,是为了共用所有原型方法
Effect.prototype = { Effect.prototype = {
contrustor: Effect, contrustor: Effect,
@ -4395,7 +4407,7 @@ Effect.prototype = {
callEffectHook(me, "abort" + upperFirstChar(oppositeName)) callEffectHook(me, "abort" + upperFirstChar(oppositeName))
callEffectHook(me, "before" + upperFirstChar(name)) callEffectHook(me, "before" + upperFirstChar(name))
if (!isLeave) if (!isLeave)
before(el) // 这里可能做插入DOM树的操作,因此必须在修改类名前执行 before(el) //  这里可能做插入DOM树的操作,因此必须在修改类名前执行
var cssCallback = function (cancel) { var cssCallback = function (cancel) {
el.removeEventListener(me.cssEvent, me.cssCallback) el.removeEventListener(me.cssEvent, me.cssCallback)
if (isLeave) { if (isLeave) {
@ -4622,7 +4634,7 @@ avalon.directive("if", {
elem.required = false elem.required = false
elem.setAttribute("_required", "true") elem.setAttribute("_required", "true")
} }
try {// 如果不支持querySelectorAll或:required,可以直接无视 try {// 如果不支持querySelectorAll或:required,可以直接无视
avalon.each(elem.querySelectorAll(":required"), function (el) { avalon.each(elem.querySelectorAll(":required"), function (el) {
elem.required = false elem.required = false
el.setAttribute("_required", "true") el.setAttribute("_required", "true")
@ -4779,7 +4791,7 @@ avalon.directive("include", {
return nodesToFrag(nodes) return nodesToFrag(nodes)
} }
} else { } else {
before = function () {// 新添加元素的动画 before = function () {// 新添加元素的动画 
target.insertBefore(fragment, binding.end) target.insertBefore(fragment, binding.end)
scanNodeArray(nodes, vmodels) scanNodeArray(nodes, vmodels)
} }
@ -4906,7 +4918,6 @@ avalon.directive("repeat", {
effectBinding(elem, binding) effectBinding(elem, binding)
binding.param = binding.param || "el" binding.param = binding.param || "el"
binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels)
// binding.renderedCallback =
var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels)
var signature = generateID(type) var signature = generateID(type)
@ -4984,13 +4995,18 @@ avalon.directive("repeat", {
var keyOrId = track[i] //array为随机数, object 为keyName var keyOrId = track[i] //array为随机数, object 为keyName
var proxy = retain[keyOrId] var proxy = retain[keyOrId]
if (!proxy) { if (!proxy) {
proxy = getProxyVM(this)
proxy = getProxyVM(this)
proxy.$up = null
if (xtype === "array") { if (xtype === "array") {
action = "add" action = "add"
proxy.$id = keyOrId proxy.$id = keyOrId
var valueItem = value[i]
proxy[param] = value[i] //index proxy[param] = valueItem //index
if(Object(valueItem) === valueItem){
valueItem.$ups = valueItem.$ups || {}
valueItem.$ups[param] = proxy
}
} else { } else {
action = "append" action = "append"
@ -5011,7 +5027,7 @@ avalon.directive("repeat", {
} }
//重写proxy //重写proxy
if (this.enterCount === 1) {// 防止多次进入,导致位置不对 if (this.enterCount === 1) {// 防止多次进入,导致位置不对
proxy.$active = false proxy.$active = false
proxy.$oldIndex = proxy.$index proxy.$oldIndex = proxy.$index
proxy.$active = true proxy.$active = true
@ -5024,7 +5040,7 @@ avalon.directive("repeat", {
proxy.$last = i === length - 1 proxy.$last = i === length - 1
// proxy[param] = value[i] // proxy[param] = value[i]
} else { } else {
proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况
} }
proxies.push(proxy) proxies.push(proxy)
} }
@ -5075,7 +5091,7 @@ avalon.directive("repeat", {
} else if (proxy.$index !== proxy.$oldIndex) { } else if (proxy.$index !== proxy.$oldIndex) {
(function (proxy2, preElement) { (function (proxy2, preElement) {
staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () {
var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了
var inserted = avalon.slice(curNode.childNodes) var inserted = avalon.slice(curNode.childNodes)
parent.insertBefore(curNode, preElement.nextSibling) parent.insertBefore(curNode, preElement.nextSibling)
animateRepeat(inserted, 1, binding) animateRepeat(inserted, 1, binding)

View File

@ -29,8 +29,8 @@
<td>{{ el.created_by.username }}</td> <td>{{ el.created_by.username }}</td>
<td ms-text="el.visible?'可见':'不可见'"></td> <td ms-text="el.visible?'可见':'不可见'"></td>
<td> <td>
<a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="showEditContestArea($index+1)">编辑</a> <a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="editContest(el.id)">编辑</a>
<a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="showEditProblemArea($index+1)">题目</a> <a class="btn btn-info btn-sm" href="javascript:void(0)" ms-click="showContestProblems(el.id)">题目</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -0,0 +1,107 @@
<div ms-controller="edit_contest" class="col-md-9">
<form id="edit-contest-form">
<nav>
<ul class="pager">
<li class="previous" ms-click="showContestListPage()"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<div class="col-md-12">
<label>比赛名称</label>
</div>
<div class="col-md-12">
<div class="form-group">
<input type="text" name="name" class="form-control" ms-duplex="title"
data-error="请填写比赛名称(名称不能超过50个字)" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<label>说明</label>
</div>
<div class="col-md-12">
<div class="form-group">
<ms:editor $id="contestDescriptionEditor" config="contestDescriptionEditor"></ms:editor>
</div>
</div>
<div class="col-md-6">
<label>开始时间</label>
<div class="form-group">
<input type="text" class="form-control" name="start_time" id="contest_start_time"
ms-duplex="startTime" data-error="请填写比赛开始时间" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<label>结束时间</label>
<div class="form-group">
<input type="text" class="form-control" name="end_time" id="contest_end_time"
ms-duplex="endTime" data-error="请填写比赛结束时间" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>比赛类型</label>
<div>
<span ms-if="showGlobalViewRadio">
<label>
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
</small>
</label>
</span>
<span>
<label>
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
</small>
</label>
</span>
</div>
</div>
</div>
<div class="col-md-6" ms-visible="isGlobal">
<label>密码保护</label>
<div class="form-group">
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
</div>
</div>
<div class="form-group col-md-12" ms-visible="!isGlobal">
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
<div ms-repeat="allGroups" class="col-md-4">
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }}
</div>
</div>
<div class="col-md-6">
<label>实时排名</label>
<div class="form-group">
<label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank">
<small>不勾选则排名不更新,且只显示自己的提交。</small>
</label>
</div>
</div>
<div class="col-md-6">
<label>是否可见</label>
<div class="form-group">
<label class="text"><input type="checkbox" ms-duplex-checked="visible">
<small>可见</small>
</label>
</div>
</div>
<div class="col-md-12">
<input type="submit" class="btn btn-success btn-lg" value="发布比赛">
</div>
</form>
</div>
<script src="/static/js/app/admin/contest/editContest.js"></script>

View File

@ -2,7 +2,7 @@
<form id="edit-problem-form"> <form id="edit-problem-form">
<nav> <nav>
<ul class="pager"> <ul class="pager">
<li class="previous" ms-click="goBack(0)"><a href="javascript:void(0)"><span <li class="previous" ms-click="goBack()"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li> aria-hidden="true">&larr;</span> 返回</a></li>
</ul> </ul>
</nav> </nav>
@ -22,15 +22,14 @@
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<label>题目描述</label> <label>题目描述</label>
<textarea id="problemDescription" placeholder="这里输入内容(此内容不能为空)" ms-duplex="description"></textarea> <ms:editor $id="contestProblemDescriptionEditor" config="contestProblemDescriptionEditor"></ms:editor>
<p class="error-info" ms-visible="description==''">请填写题目描述</p>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="form-group"><label>时间限制(ms)</label> <div class="form-group"><label>时间限制(ms)</label>
<input type="number" name="timeLimit" class="form-control" ms-duplex="timeLimit" <input type="number" name="timeLimit" class="form-control" ms-duplex="timeLimit"
data-error="请输入时间限制(保证是一个1000-5000的合法整数)" required> data-error="请输入时间限制(保证是一个30-5000的合法整数)" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
</div> </div>
@ -41,11 +40,6 @@
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>
</div> </div>
<div class="col-md-3" ms-visible="contestMode=='2'">
<div class="form-group"><label>分值(仅计分模式)</label>
<input type="number" name="score" class="form-control" ms-duplex="score">
</div>
</div>
<div class="col-md-3 form-group"> <div class="col-md-3 form-group">
<label>是否可见</label><br> <label>是否可见</label><br>
<label><input type="checkbox" ms-duplex-checked="visible"> <label><input type="checkbox" ms-duplex-checked="visible">
@ -103,7 +97,7 @@
</div> </div>
</div> </div>
<div class="col-md-12"><br> <div class="col-md-12"><br>
<label>测试数据(多次上传将覆盖原有测试用例)</label><br> <label>测试数据<span ms-if="uploadSuccess">(当前已上传,继续上传将覆盖原有测试用例)</span></label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br> <small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out 1.in 1.out 2.in 2.out
</small> </small>
@ -127,7 +121,7 @@
</div> </div>
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<label>提示</label> <label>提示</label>
<textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea> <ms:editor $id="contestProblemHintEditor" config="contestProblemHintEditor"></ms:editor>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<button type="submit" class="btn btn-success btn-lg">发布题目</button> <button type="submit" class="btn btn-success btn-lg">发布题目</button>

View File

@ -0,0 +1,35 @@
<div ms-controller="contestProblemList" class="col-md-9">
<nav>
<ul class="pager">
<li class="previous" ms-click="goBack()"><a href="javascript:void(0)">
<span aria-hidden="true">&larr;</span> 返回</a>
</li>
</ul>
</nav>
<h1>比赛题目列表</h1>
<div>
<button class="btn btn-primary" ms-click="addProblem()">创建题目</button>
</div>
<table class="table table-striped">
<tr>
<th>ID</th>
<th>题目</th>
<th>创建时间</th>
<th>可见</th>
<th>通过次数/提交总数</th>
<td></td>
</tr>
<tr ms-repeat="problemList">
<td>{{ el.sort_index }}</td>
<td>{{ el.title }}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>{{ el.total_accepted_number }}/{{ el.total_submit_number }}</td>
<td>
<button class="btn-sm btn-info" ms-click="showEditProblemPage(el.id)">编辑</button>
<button class="btn-sm btn-primary" ms-if="!el.is_public && adminType == 2" ms-click="makeProblemPublic(el)">公开</button>
</td>
</tr>
</table>
</div>
<script src="/static/js/app/admin/contest/contestProblemList.js"></script>

View File

@ -1,37 +0,0 @@
<div ms-controller="contestSubmissionList" class="col-md-9">
<nav>
<ul class="pager">
<li class="previous" ms-click="goBack()"><a href="javascript:void(0)"><span
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<h1>提交列表</h1>
<a href="javascript:void(0)" class="btn btn-sm btn-primary" ms-click="getPage(1)">
<span class="glyphicon glyphicon-refresh"></span> 刷新
</a>
<table class="table table-striped">
<tr>
<th>ID</th>
<th>创建时间</th>
<th>作者</th>
<td>结果</td>
<td></td>
</tr>
<tr ms-repeat="submissionList">
<td>{{ el.id }}</td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
<td>{{ el.user }}</td>
<td>{{ results[el.result] }}</td>
<td>
<a class="btn btn-info" ms-attr-href="'/submission/' + el.id + '/'" target="_blank">详情</a>
</td>
</tr>
</table>
<div class="text-right">
页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp;
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
</div>
</div>
<script src="/static/js/app/admin/contest/submissionList.js"></script>

View File

@ -104,7 +104,7 @@
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br> <small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out 1.in 1.out 2.in 2.out
</small> </small>
<p>上传进度<span ms-text="uploadProgress * 100"></span>%</p> <p>上传进度<span ms-text="uploadProgress"></span>%</p>
<table class="table table-striped" ms-visible="uploadSuccess"> <table class="table table-striped" ms-visible="uploadSuccess">
<tr> <tr>
<td>编号</td> <td>编号</td>

View File

@ -110,7 +110,7 @@
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br> <small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out 1.in 1.out 2.in 2.out
</small> </small>
<p>上传进度<span ms-text="uploadProgress * 100"></span>%</p> <p>上传进度<span ms-text="uploadProgress"></span>%</p>
<table class="table table-striped" ms-visible="uploadSuccess"> <table class="table table-striped" ms-visible="uploadSuccess">
<tr> <tr>
<td>编号</td> <td>编号</td>

View File

@ -59,7 +59,7 @@
{{ request.user.username }} {{ request.user.username }}
<span class="caret"></span></a> <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% if request.user.admin_type == 2 %} {% if request.user.admin_type > 0 %}
<li><a href="/admin/">后台管理</a></li> <li><a href="/admin/">后台管理</a></li>
{% endif %} {% endif %}
<li><a href="/submissions/">我的提交</a></li> <li><a href="/submissions/">我的提交</a></li>