mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 16:33:22 +00:00
Accept Merge Request #281 后台部分重构,还未完成;部分代码修改为 web 组件;修复部分小 bug;更新个人主页样式 : (virusdefender-dev -> dev)
Merge Request: 后台部分重构,还未完成;部分代码修改为 web 组件;修复部分小 bug;更新个人主页样式 Created By: @virusdefender Accepted By: @virusdefender URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/281
This commit is contained in:
commit
a176999750
@ -1,4 +1,5 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
import urllib
|
||||||
import functools
|
import functools
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ class BasePermissionDecorator(object):
|
|||||||
if self.request.is_ajax():
|
if self.request.is_ajax():
|
||||||
return error_response(u"请先登录")
|
return error_response(u"请先登录")
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect("/login/")
|
return HttpResponseRedirect("/login/?__from=" + urllib.quote(self.request.build_absolute_uri()))
|
||||||
|
|
||||||
def check_permission(self):
|
def check_permission(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
37
account/migrations/0013_userprofile.py
Normal file
37
account/migrations/0013_userprofile.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import jsonfield.fields
|
||||||
|
import account.models
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0012_auto_20151012_1546'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserProfile',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('avatar', models.CharField(default=account.models._random_avatar, max_length=50)),
|
||||||
|
('blog', models.URLField(null=True, blank=True)),
|
||||||
|
('mood', models.CharField(max_length=200, null=True, blank=True)),
|
||||||
|
('hduoj_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||||
|
('bestcoder_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||||
|
('codeforces_username', models.CharField(max_length=30, null=True, blank=True)),
|
||||||
|
('rank', models.IntegerField(default=65535)),
|
||||||
|
('accepted_number', models.IntegerField(default=0)),
|
||||||
|
('submissions_number', models.IntegerField(default=0)),
|
||||||
|
('problems_status', jsonfield.fields.JSONField(default={})),
|
||||||
|
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'user_profile',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -48,3 +48,26 @@ class User(AbstractBaseUser):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "user"
|
db_table = "user"
|
||||||
|
|
||||||
|
|
||||||
|
def _random_avatar():
|
||||||
|
import random
|
||||||
|
return "/static/img/avatar/avatar-" + str(random.randint(1, 20)) + ".png"
|
||||||
|
|
||||||
|
|
||||||
|
class UserProfile(models.Model):
|
||||||
|
user = models.OneToOneField(User)
|
||||||
|
avatar = models.CharField(max_length=50, default=_random_avatar)
|
||||||
|
blog = models.URLField(blank=True, null=True)
|
||||||
|
mood = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
hduoj_username = models.CharField(max_length=30, blank=True, null=True)
|
||||||
|
bestcoder_username = models.CharField(max_length=30, blank=True, null=True)
|
||||||
|
codeforces_username = models.CharField(max_length=30, blank=True, null=True)
|
||||||
|
rank = models.IntegerField(default=65535)
|
||||||
|
accepted_number = models.IntegerField(default=0)
|
||||||
|
submissions_number = models.IntegerField(default=0)
|
||||||
|
# JSON字典用来表示该用户的问题的解决状态 1为ac,2为正在进行
|
||||||
|
problems_status = JSONField(default={})
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "user_profile"
|
||||||
|
@ -58,3 +58,7 @@ class ResetPasswordSerializer(serializers.Serializer):
|
|||||||
token = serializers.CharField(min_length=1, max_length=40)
|
token = serializers.CharField(min_length=1, max_length=40)
|
||||||
password = serializers.CharField(min_length=6, max_length=30)
|
password = serializers.CharField(min_length=6, max_length=30)
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||||
|
|
||||||
|
|
||||||
|
class SSOSerializer(serializers.Serializer):
|
||||||
|
token = serializers.CharField(max_length=40)
|
@ -5,6 +5,7 @@ from django.contrib import auth
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
from django.core.exceptions import MultipleObjectsReturned
|
from django.core.exceptions import MultipleObjectsReturned
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
@ -20,7 +21,8 @@ from .models import User
|
|||||||
from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
|
from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
|
||||||
UserRegisterSerializer, UserChangePasswordSerializer,
|
UserRegisterSerializer, UserChangePasswordSerializer,
|
||||||
EmailCheckSerializer, UserSerializer, EditUserSerializer,
|
EmailCheckSerializer, UserSerializer, EditUserSerializer,
|
||||||
ApplyResetPasswordSerializer, ResetPasswordSerializer)
|
ApplyResetPasswordSerializer, ResetPasswordSerializer,
|
||||||
|
SSOSerializer)
|
||||||
from .decorators import super_admin_required
|
from .decorators import super_admin_required
|
||||||
|
|
||||||
|
|
||||||
@ -284,15 +286,37 @@ class ResetPasswordAPIView(APIView):
|
|||||||
|
|
||||||
|
|
||||||
def user_index_page(request, username):
|
def user_index_page(request, username):
|
||||||
return render(request, "oj/account/user_index.html")
|
try:
|
||||||
|
user = User.objects.get(username=username)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return error_page(request, u"用户不存在")
|
||||||
|
|
||||||
|
blog_link = ""
|
||||||
|
|
||||||
|
if user.userprofile.blog:
|
||||||
|
blog_link = user.userprofile.blog.replace("http://", "").replace("https://", "")
|
||||||
|
|
||||||
|
return render(request, "oj/account/user_index.html", {"user": user, "blog_link": blog_link})
|
||||||
|
|
||||||
|
|
||||||
def auth_page(request):
|
class SSOAPIView(APIView):
|
||||||
if not request.user.is_authenticated():
|
def post(self, request):
|
||||||
return render(request, "oj/account/oauth.html")
|
serializer = SSOSerializer(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
try:
|
||||||
|
user = User.objects.get(auth_token=serializer.data["token"])
|
||||||
|
return success_response({"username": user.username})
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return error_response(u"用户不存在")
|
||||||
|
else:
|
||||||
|
return serializer_invalid_response(serializer)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def get(self, request):
|
||||||
callback = request.GET.get("callback", None)
|
callback = request.GET.get("callback", None)
|
||||||
if not callback:
|
if not callback or callback != settings.SSO["callback"]:
|
||||||
return error_page(request, u"参数错误")
|
return error_page(request, u"参数错误")
|
||||||
token = rand_str()
|
token = rand_str()
|
||||||
request.user.auth_token = token
|
request.user.auth_token = token
|
||||||
return render(request, "oj/account/oauth.html", {"callback": callback, "token": token})
|
request.user.save()
|
||||||
|
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
|
@ -1,4 +1,5 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
import urllib
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
@ -30,7 +31,7 @@ def check_user_contest_permission(func):
|
|||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
return error_response(u"请先登录")
|
return error_response(u"请先登录")
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect("/login/")
|
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.build_absolute_uri()))
|
||||||
|
|
||||||
# kwargs 就包含了 url 里面的参数
|
# kwargs 就包含了 url 里面的参数
|
||||||
if "contest_id" in kwargs:
|
if "contest_id" in kwargs:
|
||||||
|
@ -11,10 +11,8 @@ from .models import Contest, ContestProblem
|
|||||||
class CreateContestSerializer(serializers.Serializer):
|
class CreateContestSerializer(serializers.Serializer):
|
||||||
title = serializers.CharField(max_length=40)
|
title = serializers.CharField(max_length=40)
|
||||||
description = serializers.CharField(max_length=5000)
|
description = serializers.CharField(max_length=5000)
|
||||||
mode = serializers.IntegerField()
|
|
||||||
contest_type = serializers.IntegerField()
|
contest_type = serializers.IntegerField()
|
||||||
real_time_rank = serializers.BooleanField()
|
real_time_rank = serializers.BooleanField()
|
||||||
show_user_submission = serializers.BooleanField()
|
|
||||||
password = serializers.CharField(max_length=30, required=False, default=None)
|
password = serializers.CharField(max_length=30, required=False, default=None)
|
||||||
start_time = serializers.DateTimeField()
|
start_time = serializers.DateTimeField()
|
||||||
end_time = serializers.DateTimeField()
|
end_time = serializers.DateTimeField()
|
||||||
@ -45,10 +43,8 @@ class EditContestSerializer(serializers.Serializer):
|
|||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
title = serializers.CharField(max_length=40)
|
title = serializers.CharField(max_length=40)
|
||||||
description = serializers.CharField(max_length=10000)
|
description = serializers.CharField(max_length=10000)
|
||||||
mode = serializers.IntegerField()
|
|
||||||
contest_type = serializers.IntegerField()
|
contest_type = serializers.IntegerField()
|
||||||
real_time_rank = serializers.BooleanField()
|
real_time_rank = serializers.BooleanField()
|
||||||
show_user_submission = serializers.BooleanField()
|
|
||||||
password = serializers.CharField(max_length=30, required=False, default=None)
|
password = serializers.CharField(max_length=30, required=False, default=None)
|
||||||
start_time = serializers.DateTimeField()
|
start_time = serializers.DateTimeField()
|
||||||
end_time = serializers.DateTimeField()
|
end_time = serializers.DateTimeField()
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
# coding=utf-8
|
|
||||||
from django.conf.urls import include, url
|
|
||||||
from django.views.generic import TemplateView
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
|
|
||||||
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
|
|
||||||
]
|
|
@ -64,9 +64,8 @@ class ContestAdminAPIView(APIView):
|
|||||||
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
|
return error_response(u"比赛的开始时间必须早于比赛结束的时间")
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.create(title=data["title"], description=data["description"],
|
contest = Contest.objects.create(title=data["title"], description=data["description"],
|
||||||
mode=data["mode"], contest_type=data["contest_type"],
|
contest_type=data["contest_type"],
|
||||||
real_time_rank=data["real_time_rank"], password=data["password"],
|
real_time_rank=data["real_time_rank"], password=data["password"],
|
||||||
show_user_submission=data["show_user_submission"],
|
|
||||||
start_time=dateparse.parse_datetime(data["start_time"]),
|
start_time=dateparse.parse_datetime(data["start_time"]),
|
||||||
end_time=dateparse.parse_datetime(data["end_time"]),
|
end_time=dateparse.parse_datetime(data["end_time"]),
|
||||||
created_by=request.user, visible=data["visible"])
|
created_by=request.user, visible=data["visible"])
|
||||||
@ -125,10 +124,8 @@ class ContestAdminAPIView(APIView):
|
|||||||
|
|
||||||
contest.title = data["title"]
|
contest.title = data["title"]
|
||||||
contest.description = data["description"]
|
contest.description = data["description"]
|
||||||
contest.mode = data["mode"]
|
|
||||||
contest.contest_type = data["contest_type"]
|
contest.contest_type = data["contest_type"]
|
||||||
contest.real_time_rank = data["real_time_rank"]
|
contest.real_time_rank = data["real_time_rank"]
|
||||||
contest.show_user_submission = data["show_user_submission"]
|
|
||||||
contest.start_time = dateparse.parse_datetime(data["start_time"])
|
contest.start_time = dateparse.parse_datetime(data["start_time"])
|
||||||
contest.end_time = dateparse.parse_datetime(data["end_time"])
|
contest.end_time = dateparse.parse_datetime(data["end_time"])
|
||||||
contest.visible = data["visible"]
|
contest.visible = data["visible"]
|
||||||
@ -225,6 +222,7 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
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.score = data["score"]
|
||||||
|
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)
|
||||||
else:
|
else:
|
||||||
@ -486,6 +484,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
|
|||||||
if user_id:
|
if user_id:
|
||||||
submissions = submissions.filter(user_id=request.GET.get("user_id"))
|
submissions = submissions.filter(user_id=request.GET.get("user_id"))
|
||||||
|
|
||||||
|
problem_id = request.GET.get("problem_id", None)
|
||||||
|
if problem_id:
|
||||||
|
submissions = submissions.filter(problem_id=problem_id)
|
||||||
|
|
||||||
# 封榜的时候只能看到自己的提交
|
# 封榜的时候只能看到自己的提交
|
||||||
if not contest.real_time_rank:
|
if not contest.real_time_rank:
|
||||||
if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
|
if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
|
||||||
@ -535,4 +537,4 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
|
|||||||
return render(request, "oj/contest/submissions_list.html",
|
return render(request, "oj/contest/submissions_list.html",
|
||||||
{"submissions": current_page, "page": int(page),
|
{"submissions": current_page, "page": int(page),
|
||||||
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
|
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
|
||||||
"contest": contest, "filter": filter, "user_id": user_id})
|
"contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id})
|
@ -11,18 +11,13 @@ DATABASES = {
|
|||||||
},
|
},
|
||||||
# submission 的 name 和 engine 请勿修改,其他代码会用到
|
# submission 的 name 和 engine 请勿修改,其他代码会用到
|
||||||
'submission': {
|
'submission': {
|
||||||
'NAME': 'oj_submission',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
|
||||||
'CONN_MAX_AGE': 0.1,
|
|
||||||
'HOST': "127.0.0.1",
|
|
||||||
'PORT': 3306,
|
|
||||||
'USER': 'root',
|
|
||||||
'PASSWORD': 'root',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
REDIS_CACHE = {
|
REDIS_CACHE = {
|
||||||
"host": "121.42.32.129",
|
"host": "127.0.0.1",
|
||||||
"port": 6379,
|
"port": 6379,
|
||||||
"db": 1
|
"db": 1
|
||||||
}
|
}
|
||||||
@ -37,3 +32,5 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/src/"), BASE_DIR]
|
|||||||
|
|
||||||
# 模板文件夹
|
# 模板文件夹
|
||||||
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')]
|
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')]
|
||||||
|
|
||||||
|
SSO = {"callback": "http://localhost:8765/login"}
|
@ -43,3 +43,5 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/release/"), os.path.join(BASE
|
|||||||
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')]
|
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')]
|
||||||
|
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
|
||||||
|
SSO = {"callback": "https://discuss.acmer.site/login"}
|
||||||
|
@ -6,7 +6,7 @@ from django.views.generic import TemplateView
|
|||||||
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
|
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
|
||||||
UserChangePasswordAPIView, EmailCheckAPIView,
|
UserChangePasswordAPIView, EmailCheckAPIView,
|
||||||
UserAdminAPIView, UserInfoAPIView,
|
UserAdminAPIView, UserInfoAPIView,
|
||||||
ApplyResetPasswordAPIView)
|
ApplyResetPasswordAPIView, SSOAPIView)
|
||||||
|
|
||||||
from announcement.views import AnnouncementAdminAPIView
|
from announcement.views import AnnouncementAdminAPIView
|
||||||
|
|
||||||
@ -121,13 +121,13 @@ urlpatterns = [
|
|||||||
url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"),
|
url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"),
|
||||||
url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"),
|
url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"),
|
||||||
|
|
||||||
url(r'^user/(?P<username>\w+)/$', "account.views.user_index_page"),
|
url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
|
||||||
|
|
||||||
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
|
||||||
|
|
||||||
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
|
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
|
||||||
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
|
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
|
||||||
url(r'^account/auth/$', "account.views.auth_page", name="auth_login_page"),
|
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,3 +62,32 @@ pre, code {
|
|||||||
font-family: "source_code_pro";
|
font-family: "source_code_pro";
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#index-avatar{
|
||||||
|
height: 200px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-mood{
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#oj-logo{
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.super-admin-star{
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-star{
|
||||||
|
color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-data-number{
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-data-text{
|
||||||
|
display: block;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
|
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
|
||||||
"validator"],
|
"validator", "editorComponent"],
|
||||||
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
|
||||||
|
|
||||||
$("#add-contest-form").validator().on('submit', function (e) {
|
$("#add-contest-form").validator().on('submit', function (e) {
|
||||||
@ -7,11 +7,9 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var ajaxData = {
|
var ajaxData = {
|
||||||
title: vm.title,
|
title: vm.title,
|
||||||
description: vm.description,
|
description: avalon.vmodels.contestDescriptionEditor.content,
|
||||||
mode: vm.mode,
|
|
||||||
contest_type: 0,
|
contest_type: 0,
|
||||||
real_time_rank: vm.realTimeRank,
|
real_time_rank: vm.realTimeRank,
|
||||||
show_user_submission: vm.showSubmission,
|
|
||||||
start_time: vm.startTime,
|
start_time: vm.startTime,
|
||||||
end_time: vm.endTime,
|
end_time: vm.endTime,
|
||||||
visible: false
|
visible: false
|
||||||
@ -38,12 +36,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
bsAlert("你没有选择参赛用户!");
|
bsAlert("你没有选择参赛用户!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (vm.editDescription == "") {
|
if (ajaxData.description.trim() == "") {
|
||||||
bsAlert("比赛描述不能为空!");
|
bsAlert("比赛描述不能为空!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$.ajax({ // Add contest
|
$.ajax({
|
||||||
beforeSend: csrfTokenHeader,
|
|
||||||
url: "/api/admin/contest/",
|
url: "/api/admin/contest/",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
contentType: "application/json;charset=UTF-8",
|
contentType: "application/json;charset=UTF-8",
|
||||||
@ -52,17 +49,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.code) {
|
if (!data.code) {
|
||||||
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
|
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
|
||||||
vm.title = "";
|
|
||||||
vm.description = "";
|
|
||||||
vm.startTime = "";
|
|
||||||
vm.endTime = "";
|
|
||||||
vm.password = "";
|
|
||||||
vm.mode = "0";
|
|
||||||
vm.showSubmission = true;
|
|
||||||
location.hash = "#contest/contest_list";
|
location.hash = "#contest/contest_list";
|
||||||
vm.isGlobal = true;
|
|
||||||
vm.allGroups = [];
|
|
||||||
vm.showGlobalViewRadio = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bsAlert(data.data);
|
bsAlert(data.data);
|
||||||
@ -73,23 +60,25 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
editor("#editor");
|
//editor("#editor");
|
||||||
if (avalon.vmodels.add_contest)
|
if (avalon.vmodels.add_contest)
|
||||||
var vm = avalon.vmodels.add_contest;
|
var vm = avalon.vmodels.add_contest;
|
||||||
else
|
else
|
||||||
var vm = avalon.define({
|
var vm = avalon.define({
|
||||||
$id: "add_contest",
|
$id: "add_contest",
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
|
||||||
startTime: "",
|
startTime: "",
|
||||||
endTime: "",
|
endTime: "",
|
||||||
password: "",
|
password: "",
|
||||||
mode: "0",
|
|
||||||
showSubmission: true,
|
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
allGroups: [],
|
allGroups: [],
|
||||||
showGlobalViewRadio: true,
|
showGlobalViewRadio: true,
|
||||||
realTimeRank: true
|
realTimeRank: true,
|
||||||
|
|
||||||
|
contestDescriptionEditor: {
|
||||||
|
editorId: "contest-description-editor",
|
||||||
|
placeholder: "比赛介绍内容"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -102,7 +91,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
if (data.data.admin_type == 1) {
|
if (data.data.admin_type == 1) {
|
||||||
vm.isGlobal = false;
|
vm.isGlobal = false;
|
||||||
vm.showGlobalViewRadio = false;
|
vm.showGlobalViewRadio = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -1,277 +1,38 @@
|
|||||||
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
|
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator", "pager"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
|
||||||
|
|
||||||
avalon.ready(function () {
|
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) {
|
if (avalon.vmodels.contestList) {
|
||||||
// this page has been loaded before, so set the default value
|
|
||||||
var vm = avalon.vmodels.contestList;
|
var vm = avalon.vmodels.contestList;
|
||||||
vm.contestList = [];
|
vm.contestList = [];
|
||||||
vm.previousPage = 0;
|
|
||||||
vm.nextPage = 0;
|
|
||||||
vm.page = 1;
|
|
||||||
vm.totalPage = 1;
|
|
||||||
vm.keyword = "";
|
|
||||||
vm.editingContestId = 0;
|
|
||||||
vm.editTitle = "";
|
|
||||||
vm.editDescription = "";
|
|
||||||
vm.editProblemList = [];
|
|
||||||
vm.editPassword = "";
|
|
||||||
vm.editStartTime = "";
|
|
||||||
vm.editEndTime = "";
|
|
||||||
vm.editMode = "";
|
|
||||||
vm.editShowSubmission = false;
|
|
||||||
vm.editVisible = false;
|
|
||||||
vm.editingProblemContestIndex = 0;
|
|
||||||
vm.editRealTimeRank = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var vm = avalon.define({
|
var vm = avalon.define({
|
||||||
$id: "contestList",
|
$id: "contestList",
|
||||||
contestList: [],
|
contestList: [],
|
||||||
previousPage: 0,
|
|
||||||
nextPage: 0,
|
|
||||||
page: 1,
|
|
||||||
totalPage: 1,
|
|
||||||
showVisibleOnly: false,
|
|
||||||
keyword: "",
|
keyword: "",
|
||||||
editingContestId: 0,
|
showVisibleOnly: false,
|
||||||
editTitle: "",
|
pager: {
|
||||||
editDescription: "",
|
getPage: function(page){
|
||||||
editProblemList: [],
|
getPage(page);
|
||||||
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++) {
|
search: function () {
|
||||||
if (vm.allGroups[index].id == id) {
|
getPage(1);
|
||||||
vm.allGroups[index].isSelected = true;
|
avalon.vmodels.contestListPager.currentPage = 1;
|
||||||
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
|
vm.$watch("showVisibleOnly", function () {
|
||||||
$("#contest_start_time").datetimepicker({
|
getPage(1);
|
||||||
format: "yyyy-mm-dd hh:ii",
|
avalon.vmodels.contestListPager.currentPage = 1;
|
||||||
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) {
|
function getPage(page) {
|
||||||
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10";
|
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=20";
|
||||||
if (vm.showVisibleOnly)
|
if (vm.showVisibleOnly)
|
||||||
url += "&visible=true"
|
url += "&visible=true";
|
||||||
if (vm.keyword != "")
|
if (vm.keyword != "")
|
||||||
url += "&keyword=" + vm.keyword;
|
url += "&keyword=" + vm.keyword;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -281,10 +42,8 @@ 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.totalPage = data.data.total_page;
|
vm.announcementList = data.data.results;
|
||||||
vm.previousPage = data.data.previous_page;
|
avalon.vmodels.contestListPager.totalPage = data.data.total_page;
|
||||||
vm.nextPage = data.data.next_page;
|
|
||||||
vm.page = page;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bsAlert(data.data);
|
bsAlert(data.data);
|
||||||
@ -293,46 +52,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
avalon.scan();
|
||||||
});
|
});
|
321
static/src/js/app/admin/contest/contestList1.js
Normal file
321
static/src/js/app/admin/contest/contestList1.js
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
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();
|
||||||
|
});
|
@ -14,21 +14,18 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
method: "post",
|
method: "post",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (!data.code) {
|
if (!data.code) {
|
||||||
//成功登陆
|
function getLocationVal(id){
|
||||||
var ref = document.referrer;
|
var temp = unescape(location.search).split(id+"=")[1] || "";
|
||||||
if (ref) {
|
return temp.indexOf("&")>=0 ? temp.split("&")[0] : temp;
|
||||||
// 注册页和本页的来源的跳转回首页,防止死循环
|
}
|
||||||
if (ref.indexOf("register") > -1 || ref.indexOf("login") > -1) {
|
var from = getLocationVal("__from");
|
||||||
|
if(from != ""){
|
||||||
|
console.log(from);
|
||||||
|
window.location.href = from;
|
||||||
|
}
|
||||||
|
else{
|
||||||
location.href = "/";
|
location.href = "/";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// 判断来源,只有同域下才跳转
|
|
||||||
if (ref.split("/")[2].split(":")[0] == location.hostname) {
|
|
||||||
location.href = ref;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
location.href = "/";
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
refresh_captcha();
|
refresh_captcha();
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
// ------ admin web 组件 ----------
|
// ------ admin web 组件 ----------
|
||||||
pager: "components/pager",
|
pager: "components/pager",
|
||||||
|
editorComponent: "components/editorComponent",
|
||||||
|
|
||||||
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
|
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
|
||||||
//富文本编辑器simditor -> editor
|
//富文本编辑器simditor -> editor
|
||||||
@ -153,7 +154,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "submissionList_21_pack"
|
name: "submissionList_21_pack"
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
optimizeCss: "standard",
|
optimizeCss: "standard",
|
||||||
})
|
})
|
@ -17,8 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea>
|
<ms:editor $id="contestDescriptionEditor" config="contestDescriptionEditor"></ms:editor>
|
||||||
<p class="error-info" ms-visible="description==''">请填写比赛描述</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@ -39,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>可见范围</label>
|
<label>比赛类型</label>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span ms-if="showGlobalViewRadio">
|
<span ms-if="showGlobalViewRadio">
|
||||||
@ -70,30 +69,12 @@
|
|||||||
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
|
||||||
<label>排名方式</label>
|
<div class="col-md-12">
|
||||||
<div class="form-group">
|
|
||||||
<label><input type="radio" name="mode" ms-duplex-string="mode" value="0">
|
|
||||||
<small>ACM</small>
|
|
||||||
</label>
|
|
||||||
<label><input type="radio" name="mode" ms-duplex-string="mode" value="1">
|
|
||||||
<small>分数</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label>公开提交记录</label>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="text"><input type="checkbox" ms-duplex-checked="showSubmission">
|
|
||||||
<small>公开</small>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<label>实时排名</label>
|
<label>实时排名</label>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank">
|
<label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank">
|
||||||
<small>是</small>
|
<small>如果不勾选,排名会被缓存,不会更新,而且只显示自己的提交。用于 acm 封榜。</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<div class="form-group-sm">
|
<div class="form-group-sm">
|
||||||
<label>搜索</label>
|
<label>搜索</label>
|
||||||
<input name="keyWord" class="form-control" placeholder="请输入关键词" ms-duplex="keyword">
|
<input name="keyWord" class="form-control" placeholder="请输入关键词" ms-duplex="keyword">
|
||||||
<input type="submit" value="搜索" class="btn btn-primary" ms-click="getPage(1)">
|
<input type="submit" value="搜索" class="btn btn-primary" ms-click="search()">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<br>
|
<br>
|
||||||
@ -38,10 +38,10 @@
|
|||||||
<label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label>
|
<label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
页数:{{ page }}/{{ totalPage }}
|
<ms:pager $id="contestListPager" config="pager"></ms:pager>
|
||||||
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
|
||||||
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<div ms-visible="editingContestId">
|
<div ms-visible="editingContestId">
|
||||||
<form id="edit-contest-form">
|
<form id="edit-contest-form">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@ -108,7 +108,7 @@
|
|||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
|
|
||||||
<div ms-repeat="allGroups" class="col-md-4">
|
<div ms-repeat="allGroups" class="col-md-4">
|
||||||
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected"> {{ el.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -186,6 +186,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/js/app/admin/contest/contestList.js"></script>
|
<script src="/static/js/app/admin/contest/contestList.js"></script>
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
{% extends "oj_base.html" %}
|
|
||||||
{% block title %}
|
|
||||||
授权登录
|
|
||||||
{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<div class="container main">
|
|
||||||
<div class="text-center">
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<p>3秒钟后将跳转到<span id="link">{{ callback }}</span></p>
|
|
||||||
<script>setTimeout(function(){
|
|
||||||
window.location.href = "{{ callback }}?token={{ token }}"},
|
|
||||||
3000);
|
|
||||||
</script>
|
|
||||||
{% else %}
|
|
||||||
<script>window.location.href = "/login/";</script>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block js_block %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
20
template/src/oj/account/sso.html
Normal file
20
template/src/oj/account/sso.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "oj_base.html" %}
|
||||||
|
{% block title %}
|
||||||
|
授权登录
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="container main">
|
||||||
|
<div class="text-center">
|
||||||
|
<p>3秒钟后将使用账号{{ request.user.username }}登录<span id="link">{{ callback }}</span></p>
|
||||||
|
<button class="btn btn-warning" onclick="location.href='/'">取消登录</button>
|
||||||
|
<button class="btn btn-success" onclick="location.href='/login/'">更换账号</button>
|
||||||
|
<script>setTimeout(function(){
|
||||||
|
window.location.href = "{{ redirect_url }}"},
|
||||||
|
3000);
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block js_block %}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,90 +1,83 @@
|
|||||||
{% extends "oj_base.html" %}
|
{% extends "oj_base.html" %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
|
{{ user.username }}的主页
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="container main">
|
<div class="container main">
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive"
|
<img src="{{ user.userprofile.avatar }}" class="img-responsive" id="index-avatar">
|
||||||
style="height: 200px;width: 200px;">
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>virusdefender</h2>
|
<h2>
|
||||||
|
{{ user.username }}
|
||||||
|
{% ifequal user.admin_type 2 %}
|
||||||
|
<span class="glyphicon glyphicon-star super-admin-star" title="超级管理员"></span>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal user.admin_type 1 %}
|
||||||
|
<span class="glyphicon glyphicon-star admin-star"></span>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group col-lg-10">
|
<div class="list-group col-lg-9">
|
||||||
|
|
||||||
|
{% if user.userprofile.blog %}
|
||||||
<p class="list-group-item"><span class="glyphicon glyphicon-link"></span>
|
<p class="list-group-item"><span class="glyphicon glyphicon-link"></span>
|
||||||
<a href="https://virusdefender.net">https://virusdefender.net</a>
|
<a href="{{ user.userprofile.blog }}" target="_blank">{{ blog_link }}</a>
|
||||||
</p>
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.userprofile.hduoj_username %}
|
||||||
|
<p class="list-group-item">
|
||||||
|
<img src="/static/img/oj_logo/hdu_logo.png" id="oj-logo">
|
||||||
|
<a href="http://bestcoder.hdu.edu.cn/rating.php?user={{ user.userprofile.hduoj_username }}" target="_blank">
|
||||||
|
{{ user.userprofile.hduoj_username }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.userprofile.bestcoder_username %}
|
||||||
|
<p class="list-group-item">
|
||||||
|
<img src="/static/img/oj_logo/bestcoder_logo.png" id="oj-logo">
|
||||||
|
<a href="http://bestcoder.hdu.edu.cn/rating.php?user={{ user.userprofile.bestcoder_username }}" target="_blank">
|
||||||
|
{{ user.userprofile.bestcoder_username }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.userprofile.codeforces_username %}
|
||||||
|
<p class="list-group-item">
|
||||||
|
<img src="/static/img/oj_logo/codeforces_logo.png" id="oj-logo">
|
||||||
|
<a href="http://codeforces.com/profile/{{ user.userprofile.codeforces_username }}" target="_blank">
|
||||||
|
{{ user.userprofile.codeforces_username }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<p class="list-group-item">
|
<p class="list-group-item">
|
||||||
<img src="/static/img/oj_logo/hdu_logo.png" style="height: 20px">
|
<span class="glyphicon glyphicon-calendar"></span>
|
||||||
<a href="https://virusdefender.net">https://virusdefender.net</a>
|
{{ user.create_time }}
|
||||||
</p>
|
</p>
|
||||||
|
<div class="rows">
|
||||||
<p class="list-group-item">
|
<div class="col-lg-4 text-center">
|
||||||
<img src="/static/img/oj_logo/bestcoder_logo.png" style="height: 20px">
|
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
||||||
<a href="https://virusdefender.net">https://virusdefender.net</a>
|
<span id="user-data-text">Rank</span>
|
||||||
</p>
|
</div>
|
||||||
|
<div class="col-lg-4 text-center">
|
||||||
<p class="list-group-item">
|
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
||||||
<img src="/static/img/oj_logo/codeforces_logo.png" style="height: 20px">
|
<span id="user-data-text">AC</span>
|
||||||
<a href="https://virusdefender.net">https://virusdefender.net</a>
|
</div>
|
||||||
</p>
|
<div class="col-lg-4 text-center">
|
||||||
|
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
||||||
|
<span id="user-data-text">Submissions</span>
|
||||||
<p class="list-group-item"><span class="glyphicon glyphicon-calendar"></span> 2015-9-10</p>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<ul class="nav nav-tabs" style="margin: 10px;;">
|
|
||||||
<li role="presentation" class="active"><a href="#">Home</a></li>
|
|
||||||
<li role="presentation"><a href="#123">全部分享</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="panel panel-success">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">正在做的题</h3></div>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
<p class="list-group-item">
|
|
||||||
<a href="#" style="font-size: large;">problem title</a>
|
|
||||||
<span class="right">3 / 10</span>
|
|
||||||
<span style="display: block;">Accepted</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="list-group-item">
|
|
||||||
<a href="#" style="font-size: large;">problem title</a>
|
|
||||||
<span class="right">3 / 10</span>
|
|
||||||
<span style="display: block;">Accepted</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="list-group-item">
|
|
||||||
<a href="#" style="font-size: large;">problem title</a>
|
|
||||||
<span class="right">3 / 10</span>
|
|
||||||
<span style="display: block;">Accepted</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="list-group-item">
|
|
||||||
<a href="#" style="font-size: large;">problem title</a>
|
|
||||||
<span class="right">3 / 10</span>
|
|
||||||
<span style="display: block;">Accepted</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="panel panel-info">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">分享的代码</h3></div>
|
|
||||||
<div class="panel-body"> Panel content</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js_block %}
|
|
||||||
<script src="/static/js/app/oj/account/register.js"></script>
|
|
||||||
{% endblock %}
|
|
@ -40,7 +40,7 @@
|
|||||||
<th class="text-center">用时 + 罚时</th>
|
<th class="text-center">用时 + 罚时</th>
|
||||||
{% for item in contest_problems %}
|
{% for item in contest_problems %}
|
||||||
<th class="text-center">
|
<th class="text-center">
|
||||||
<a href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a>
|
<a href="/contest/{{ contest.id }}/submissions/?problem_id={{ item.id }}">{{ item.sort_index }}</a>
|
||||||
</th>
|
</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<tbody class="rank">
|
<tbody class="rank">
|
||||||
{% for item in rank %}
|
{% for item in rank %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{{ forloop.counter|add:paging_info.offset}}</th>
|
<th scope="row">{% if item.total_ac_number %}{{ forloop.counter|add:paging_info.offset}}{% else %}-{% endif %}</th>
|
||||||
<td>
|
<td>
|
||||||
<a href="/contest/{{ contest.id }}/submissions/?user_id={{ item.user__id }}">
|
<a href="/contest/{{ contest.id }}/submissions/?user_id={{ item.user__id }}">
|
||||||
{{ item.user__username }}
|
{{ item.user__username }}
|
||||||
|
@ -107,14 +107,14 @@
|
|||||||
<ul class="pager">
|
<ul class="pager">
|
||||||
{% if previous_page %}
|
{% if previous_page %}
|
||||||
<li class="previous">
|
<li class="previous">
|
||||||
<a href="/contest/{{ contest.id }}/submissions/{{ previous_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% if user_id %}&user_id={{ user_id }}{% endif %}{% else %}{% if user_id %}?user_id={{ user_id }}{% endif %}{% endif %}">
|
<a href="/contest/{{ contest.id }}/submissions/{{ previous_page }}/?{% if filter %}{{ filter.name }}={{ filter.content }}&{% endif %}{% if user_id %}user_id={{ user_id }}&{% endif %}{% if problem_id %}problem_id={{ problem_id }}&{% endif %}">
|
||||||
<span aria-hidden="true">←</span> 上一页
|
<span aria-hidden="true">←</span> 上一页
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if next_page %}
|
{% if next_page %}
|
||||||
<li class="next">
|
<li class="next">
|
||||||
<a href="/contest/{{ contest.id }}/submissions/{{ next_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% if user_id %}&user_id={{ user_id }}{% endif %}{% else %}{% if user_id %}?user_id={{ user_id }}{% endif %}{% endif %}">
|
<a href="/contest/{{ contest.id }}/submissions/{{ next_page }}/?{% if filter %}{{ filter.name }}={{ filter.content }}&{% endif %}{% if user_id %}user_id={{ user_id }}&{% endif %}{% if problem_id %}problem_id={{ problem_id }}&{% endif %}">
|
||||||
下一页 <span aria-hidden="true">→</span>
|
下一页 <span aria-hidden="true">→</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
Loading…
Reference in New Issue
Block a user