@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="Django default" uuid="4780d78e-bbdc-4708-aa57-23b7fa11976b">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<imported>true</imported>
|
||||
<remarks>$PROJECT_DIR$/videoproject/settings.py</remarks>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://127.0.0.1:3306/video</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,14 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="server.myapp.admin.myapp" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="Stylelint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (server)" project-jdk-type="Python SDK" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/videoproject-master.iml" filepath="$PROJECT_DIR$/.idea/videoproject-master.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="videoproject/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.8 (server)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1 @@
|
||||
# 初始化
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import *
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Comment)
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommentConfig(AppConfig):
|
||||
name = 'comment'
|
@ -0,0 +1,28 @@
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
from video.models import Video
|
||||
|
||||
|
||||
class CommentQuerySet(models.query.QuerySet):
|
||||
|
||||
def get_count(self):
|
||||
return self.count()
|
||||
|
||||
def get_today_count(self):
|
||||
return self.exclude(timestamp__lt=datetime.date.today()).count()
|
||||
|
||||
class Comment(models.Model):
|
||||
list_display = ("content","timestamp",)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
nickname = models.CharField(max_length=30,blank=True, null=True)
|
||||
avatar = models.CharField(max_length=100,blank=True, null=True)
|
||||
video = models.ForeignKey(Video, on_delete=models.CASCADE)
|
||||
content = models.CharField(max_length=100)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
objects = CommentQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
db_table = "v_comment"
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'comment'
|
||||
urlpatterns = [
|
||||
path('submit_comment/<int:pk>',views.submit_comment, name='submit_comment'),
|
||||
path('get_comments/', views.get_comments, name='get_comments'),
|
||||
]
|
@ -1 +0,0 @@
|
||||
Subproject commit 53d3ff190ca4690eba21dcec36ff8570b73425b1
|
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'videoproject.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,2 @@
|
||||
from django.contrib import admin
|
||||
|
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MyadminConfig(AppConfig):
|
||||
name = 'myadmin'
|
||||
|
@ -0,0 +1,156 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from users.models import User
|
||||
from video.models import Video, Classification
|
||||
|
||||
|
||||
class UserLoginForm(AuthenticationForm):
|
||||
username = forms.CharField(min_length=4,max_length=30,
|
||||
error_messages={
|
||||
'min_length': '用户名不少于4个字符',
|
||||
'max_length': '用户名不能多于30个字符',
|
||||
'required': '用户名不能为空',
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入用户名'}))
|
||||
password = forms.CharField(min_length=8,max_length=30,
|
||||
error_messages={
|
||||
'min_length': '密码不少于8个字符',
|
||||
'max_length': '密码不能多于30个字符',
|
||||
'required': '密码不能为空',
|
||||
},
|
||||
widget=forms.PasswordInput(attrs={'placeholder': '请输入密码'}))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'password']
|
||||
|
||||
error_messages = {'invalid_login': '用户名或密码错误', }
|
||||
|
||||
|
||||
class VideoPublishForm(forms.ModelForm):
|
||||
title = forms.CharField(min_length=4, max_length=200, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少4个字符',
|
||||
'max_length': '不能多于200个字符',
|
||||
'required': '标题不能为空'
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入内容'}))
|
||||
desc = forms.CharField(min_length=4, max_length=200, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少4个字符',
|
||||
'max_length': '不能多于200个字符',
|
||||
'required': '描述不能为空'
|
||||
},
|
||||
widget=forms.Textarea(attrs={'placeholder': '请输入内容'}))
|
||||
cover = forms.ImageField(required=True,
|
||||
error_messages={
|
||||
'required': '封面不能为空'
|
||||
},
|
||||
widget=forms.FileInput(attrs={'class' : 'n'}))
|
||||
status = forms.CharField(min_length=1, max_length=1, required=False,
|
||||
widget=forms.HiddenInput(attrs={'value':'0'}))
|
||||
class Meta:
|
||||
model = Video
|
||||
fields = ['title', 'desc','status', 'cover', 'classification']
|
||||
|
||||
|
||||
class VideoEditForm(forms.ModelForm):
|
||||
title = forms.CharField(min_length=4, max_length=200, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少4个字符',
|
||||
'max_length': '不能多于200个字符',
|
||||
'required': '标题不能为空'
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入内容'}))
|
||||
desc = forms.CharField(min_length=4, max_length=200, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少4个字符',
|
||||
'max_length': '不能多于200个字符',
|
||||
'required': '描述不能为空'
|
||||
},
|
||||
widget=forms.Textarea(attrs={'placeholder': '请输入内容'}))
|
||||
cover = forms.ImageField(required=True,
|
||||
error_messages={
|
||||
'required': '封面不能为空'
|
||||
},
|
||||
widget=forms.FileInput(attrs={'class' : 'n'}))
|
||||
|
||||
status = forms.CharField(min_length=1,max_length=1,required=False,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
# classification = forms.ModelChoiceField(queryset=Classification.objects.all())
|
||||
# classification = forms.CharField(min_length=1,max_length=1,required=False,
|
||||
# widget=forms.HiddenInput())
|
||||
|
||||
class Meta:
|
||||
model = Video
|
||||
fields = ['title', 'desc', 'status', 'cover','classification']
|
||||
|
||||
|
||||
|
||||
class UserAddForm(forms.ModelForm):
|
||||
username = forms.CharField(min_length=4,max_length=30,
|
||||
error_messages={
|
||||
'min_length': '用户名不少于4个字符',
|
||||
'max_length': '用户名不能多于30个字符',
|
||||
'required': '用户名不能为空',
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入用户名'}))
|
||||
password = forms.CharField(min_length=8,max_length=30,
|
||||
error_messages={
|
||||
'min_length': '密码不少于8个字符',
|
||||
'max_length': '密码不能多于30个字符',
|
||||
'required': '密码不能为空',
|
||||
},
|
||||
widget=forms.PasswordInput(attrs={'placeholder': '请输入密码'}))
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'password','is_staff' ]
|
||||
|
||||
|
||||
def username_validate(value):
|
||||
if value == "admin":
|
||||
raise ValidationError('不能编辑超级管理员')
|
||||
|
||||
|
||||
class UserEditForm(forms.ModelForm):
|
||||
username = forms.CharField(min_length=4, max_length=30, required=True,
|
||||
validators=[username_validate],
|
||||
error_messages={
|
||||
'min_length': '至少4个字符',
|
||||
'max_length': '不能多于30个字符',
|
||||
'required': '用户名不能为空'
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入用户名'}))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username','is_staff']
|
||||
|
||||
|
||||
class ClassificationAddForm(forms.ModelForm):
|
||||
title = forms.CharField(min_length=2, max_length=30, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少2个字符',
|
||||
'max_length': '不能多于30个字符',
|
||||
'required': '不能为空'
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入分类名称'}))
|
||||
class Meta:
|
||||
model = Classification
|
||||
fields = ['title', 'status' ]
|
||||
|
||||
class ClassificationEditForm(forms.ModelForm):
|
||||
title = forms.CharField(min_length=2, max_length=30, required=True,
|
||||
error_messages={
|
||||
'min_length': '至少2个字符',
|
||||
'max_length': '不能多于30个字符',
|
||||
'required': '不能为空'
|
||||
},
|
||||
widget=forms.TextInput(attrs={'placeholder': '请输入分类名称'}))
|
||||
|
||||
class Meta:
|
||||
model = Classification
|
||||
fields = ['title','status']
|
@ -0,0 +1,9 @@
|
||||
from chunked_upload.models import ChunkedUpload
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class MyChunkedUpload(ChunkedUpload):
|
||||
pass
|
||||
# Override the default ChunkedUpload to make the `user` field nullable
|
||||
MyChunkedUpload._meta.get_field('user').null = True
|
||||
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,46 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'myadmin'
|
||||
urlpatterns = [
|
||||
path('login/', views.login, name='login'),
|
||||
path('logout/', views.logout, name='logout'),
|
||||
|
||||
#----------------------总览---------------------------
|
||||
path('', views.IndexView.as_view(), name='index'),
|
||||
|
||||
#----------------------视频管理------------------------
|
||||
path('video_list/', views.VideoListView.as_view(), name='video_list'),
|
||||
path('video_add/', views.AddVideoView.as_view(), name='video_add'),
|
||||
|
||||
path('chunked_upload/', views.MyChunkedUploadView.as_view(), name='api_chunked_upload'),
|
||||
path('chunked_upload_complete/', views.MyChunkedUploadCompleteView.as_view(),name='api_chunked_upload_complete'),
|
||||
|
||||
path('video_publish/<int:pk>/', views.VideoPublishView.as_view(), name='video_publish'),
|
||||
path('video_publish_success/', views.VideoPublishSuccessView.as_view(), name='video_publish_success'),
|
||||
path('video_edit/<int:pk>/', views.VideoEditView.as_view(), name='video_edit'),
|
||||
path('video_delete/', views.video_delete, name='video_delete'),
|
||||
|
||||
#----------------------分类管理----------------------------
|
||||
path('classification_add/', views.ClassificationAddView.as_view(), name='classification_add'),
|
||||
path('classification_list/', views.ClassificationListView.as_view(), name='classification_list'),
|
||||
path('classification_edit/<int:pk>/', views.ClassificationEditView.as_view(), name='classification_edit'),
|
||||
path('classification_delete/', views.classification_delete, name='classification_delete'),
|
||||
|
||||
#----------------------评论管理----------------------------
|
||||
path('comment_list/', views.CommentListView.as_view(), name='comment_list'),
|
||||
path('comment_delete/', views.comment_delete, name='comment_delete'),
|
||||
|
||||
#----------------------用户管理-------------------------
|
||||
path('user_add/', views.UserAddView.as_view(), name='user_add'),
|
||||
path('user_list/', views.UserListView.as_view(), name='user_list'),
|
||||
path('user_edit/<int:pk>',views.UserEditView.as_view(), name='user_edit'),
|
||||
path('user_delete/', views.user_delete, name='user_delete'),
|
||||
|
||||
#-----------------------订阅通知-------------------------
|
||||
path('subscribe/', views.SubscribeView.as_view(), name='subscribe'),
|
||||
|
||||
# -----------------------用户反馈-------------------------
|
||||
path('feedback_list/', views.FeedbackListView.as_view(), name='feedback_list'),
|
||||
path('feedback_delete/', views.feedback_delete, name='feedback_delete'),
|
||||
]
|
@ -0,0 +1,369 @@
|
||||
import logging
|
||||
import smtplib
|
||||
|
||||
import datetime
|
||||
from chunked_upload.views import ChunkedUploadView, ChunkedUploadCompleteView
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import *
|
||||
from django.template.loader import render_to_string
|
||||
from django.views import generic
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from comment.models import Comment
|
||||
from helpers import get_page_list, AdminUserRequiredMixin, ajax_required, SuperUserRequiredMixin, send_html_email
|
||||
from users.models import User, Feedback
|
||||
from video.models import Video, Classification
|
||||
from .forms import UserLoginForm, VideoPublishForm, VideoEditForm, UserAddForm, UserEditForm, ClassificationAddForm, \
|
||||
ClassificationEditForm
|
||||
from .models import MyChunkedUpload
|
||||
|
||||
logger = logging.getLogger('my_logger')
|
||||
|
||||
def login(request):
|
||||
if request.method == 'POST':
|
||||
form = UserLoginForm(request=request, data=request.POST)
|
||||
if form.is_valid():
|
||||
username = form.cleaned_data.get('username')
|
||||
password = form.cleaned_data.get('password')
|
||||
user = authenticate(username=username, password=password)
|
||||
|
||||
if user is not None and user.is_staff:
|
||||
auth_login(request, user)
|
||||
return redirect('myadmin:index')
|
||||
else:
|
||||
form.add_error('', '请输入管理员账号')
|
||||
else:
|
||||
form = UserLoginForm()
|
||||
return render(request, 'myadmin/login.html', {'form': form})
|
||||
|
||||
|
||||
def logout(request):
|
||||
auth_logout(request)
|
||||
return redirect('myadmin:login')
|
||||
|
||||
|
||||
class IndexView(AdminUserRequiredMixin, generic.View):
|
||||
"""
|
||||
总览数据
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
video_count = Video.objects.get_count()
|
||||
video_has_published_count = Video.objects.get_published_count()
|
||||
video_not_published_count = Video.objects.get_not_published_count()
|
||||
user_count = User.objects.count()
|
||||
user_today_count = User.objects.exclude(date_joined__lt=datetime.date.today()).count()
|
||||
comment_count = Comment.objects.get_count()
|
||||
comment_today_count = Comment.objects.get_today_count()
|
||||
data = {"video_count": video_count,
|
||||
"video_has_published_count": video_has_published_count,
|
||||
"video_not_published_count": video_not_published_count,
|
||||
"user_count": user_count,
|
||||
"user_today_count": user_today_count,
|
||||
"comment_count": comment_count,
|
||||
"comment_today_count": comment_today_count}
|
||||
return render(self.request, 'myadmin/index.html', data)
|
||||
|
||||
|
||||
class AddVideoView(SuperUserRequiredMixin, TemplateView):
|
||||
template_name = 'myadmin/video_add.html'
|
||||
|
||||
|
||||
class MyChunkedUploadView(ChunkedUploadView):
|
||||
model = MyChunkedUpload
|
||||
field_name = 'the_file'
|
||||
|
||||
|
||||
class MyChunkedUploadCompleteView(ChunkedUploadCompleteView):
|
||||
model = MyChunkedUpload
|
||||
|
||||
def on_completion(self, uploaded_file, request):
|
||||
print('uploaded--->', uploaded_file.name)
|
||||
pass
|
||||
|
||||
def get_response_data(self, chunked_upload, request):
|
||||
video = Video.objects.create(file=chunked_upload.file)
|
||||
return {'code': 0, 'video_id': video.id, 'msg': 'success'}
|
||||
|
||||
|
||||
class VideoPublishView(SuperUserRequiredMixin, generic.UpdateView):
|
||||
model = Video
|
||||
form_class = VideoPublishForm
|
||||
template_name = 'myadmin/video_publish.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(VideoPublishView, self).get_context_data(**kwargs)
|
||||
clf_list = Classification.objects.all().values()
|
||||
clf_data = {'clf_list':clf_list}
|
||||
context.update(clf_data)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('myadmin:video_publish_success')
|
||||
|
||||
|
||||
class VideoPublishSuccessView(generic.TemplateView):
|
||||
template_name = 'myadmin/video_publish_success.html'
|
||||
|
||||
|
||||
class VideoEditView(SuperUserRequiredMixin, generic.UpdateView):
|
||||
model = Video
|
||||
form_class = VideoEditForm
|
||||
template_name = 'myadmin/video_edit.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(VideoEditView, self).get_context_data(**kwargs)
|
||||
clf_list = Classification.objects.all().values()
|
||||
clf_data = {'clf_list':clf_list}
|
||||
context.update(clf_data)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, "保存成功")
|
||||
return reverse('myadmin:video_edit', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
@ajax_required
|
||||
@require_http_methods(["POST"])
|
||||
def video_delete(request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无删除权限"})
|
||||
video_id = request.POST['video_id']
|
||||
instance = Video.objects.get(id=video_id)
|
||||
instance.delete()
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
|
||||
|
||||
class VideoListView(AdminUserRequiredMixin, generic.ListView):
|
||||
model = Video
|
||||
template_name = 'myadmin/video_list.html'
|
||||
context_object_name = 'video_list'
|
||||
paginate_by = 10
|
||||
q = ''
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super(VideoListView, self).get_context_data(**kwargs)
|
||||
paginator = context.get('paginator')
|
||||
page = context.get('page_obj')
|
||||
page_list = get_page_list(paginator, page)
|
||||
context['page_list'] = page_list
|
||||
context['q'] = self.q
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.q = self.request.GET.get("q", "")
|
||||
return Video.objects.get_search_list(self.q)
|
||||
|
||||
|
||||
class ClassificationListView(AdminUserRequiredMixin, generic.ListView):
|
||||
model = Classification
|
||||
template_name = 'myadmin/classification_list.html'
|
||||
context_object_name = 'classification_list'
|
||||
paginate_by = 10
|
||||
q = ''
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super(ClassificationListView, self).get_context_data(**kwargs)
|
||||
paginator = context.get('paginator')
|
||||
page = context.get('page_obj')
|
||||
page_list = get_page_list(paginator, page)
|
||||
context['page_list'] = page_list
|
||||
context['q'] = self.q
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.q = self.request.GET.get("q", "")
|
||||
return Classification.objects.filter(title__contains=self.q)
|
||||
|
||||
|
||||
class ClassificationAddView(SuperUserRequiredMixin, generic.View):
|
||||
def get(self, request):
|
||||
form = ClassificationAddForm()
|
||||
return render(self.request, 'myadmin/classification_add.html', {'form': form})
|
||||
|
||||
def post(self, request):
|
||||
form = ClassificationAddForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save(commit=True)
|
||||
return render(self.request, 'myadmin/classification_add_success.html')
|
||||
return render(self.request, 'myadmin/classification_add.html', {'form': form})
|
||||
|
||||
|
||||
@ajax_required
|
||||
@require_http_methods(["POST"])
|
||||
def classification_delete(request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无删除权限"})
|
||||
classification_id = request.POST['classification_id']
|
||||
instance = Classification.objects.get(id=classification_id)
|
||||
instance.delete()
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
|
||||
|
||||
class ClassificationEditView(SuperUserRequiredMixin, generic.UpdateView):
|
||||
model = Classification
|
||||
form_class = ClassificationEditForm
|
||||
template_name = 'myadmin/classification_edit.html'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, "保存成功")
|
||||
return reverse('myadmin:classification_edit', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
class CommentListView(AdminUserRequiredMixin, generic.ListView):
|
||||
model = Comment
|
||||
template_name = 'myadmin/comment_list.html'
|
||||
context_object_name = 'comment_list'
|
||||
paginate_by = 10
|
||||
q = ''
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super(CommentListView, self).get_context_data(**kwargs)
|
||||
paginator = context.get('paginator')
|
||||
page = context.get('page_obj')
|
||||
page_list = get_page_list(paginator, page)
|
||||
context['page_list'] = page_list
|
||||
context['q'] = self.q
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.q = self.request.GET.get("q", "")
|
||||
return Comment.objects.filter(content__contains=self.q).order_by('-timestamp')
|
||||
|
||||
|
||||
@ajax_required
|
||||
@require_http_methods(["POST"])
|
||||
def comment_delete(request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无删除权限"})
|
||||
comment_id = request.POST['comment_id']
|
||||
instance = Comment.objects.get(id=comment_id)
|
||||
instance.delete()
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
|
||||
|
||||
class UserListView(AdminUserRequiredMixin, generic.ListView):
|
||||
model = User
|
||||
template_name = 'myadmin/user_list.html'
|
||||
context_object_name = 'user_list'
|
||||
paginate_by = 10
|
||||
q = ''
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super(UserListView, self).get_context_data(**kwargs)
|
||||
paginator = context.get('paginator')
|
||||
page = context.get('page_obj')
|
||||
page_list = get_page_list(paginator, page)
|
||||
context['page_list'] = page_list
|
||||
context['q'] = self.q
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.q = self.request.GET.get("q", "")
|
||||
return User.objects.filter(username__contains=self.q).order_by('-date_joined')
|
||||
|
||||
|
||||
class UserAddView(SuperUserRequiredMixin, generic.View):
|
||||
def get(self, request):
|
||||
form = UserAddForm()
|
||||
return render(self.request, 'myadmin/user_add.html', {'form': form})
|
||||
|
||||
def post(self, request):
|
||||
form = UserAddForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save(commit=False)
|
||||
password = form.cleaned_data.get('password')
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return render(self.request, 'myadmin/user_add_success.html')
|
||||
return render(self.request, 'myadmin/user_add.html', {'form': form})
|
||||
|
||||
|
||||
class UserEditView(SuperUserRequiredMixin, generic.UpdateView):
|
||||
model = User
|
||||
form_class = UserEditForm
|
||||
template_name = 'myadmin/user_edit.html'
|
||||
|
||||
def get_success_url(self):
|
||||
messages.success(self.request, "保存成功")
|
||||
return reverse('myadmin:user_edit', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
@ajax_required
|
||||
@require_http_methods(["POST"])
|
||||
def user_delete(request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无删除权限"})
|
||||
user_id = request.POST['user_id']
|
||||
instance = User.objects.get(id=user_id)
|
||||
if instance.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "不能删除管理员"})
|
||||
instance.delete()
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
|
||||
|
||||
class SubscribeView(SuperUserRequiredMixin, generic.View):
|
||||
|
||||
def get(self, request):
|
||||
video_list = Video.objects.get_published_list()
|
||||
return render(request, "myadmin/subscribe.html" ,{'video_list':video_list})
|
||||
|
||||
def post(self, request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无权限"})
|
||||
video_id = request.POST['video_id']
|
||||
video = Video.objects.get(id=video_id)
|
||||
subject = video.title
|
||||
context = {'video': video,'site_url':settings.SITE_URL}
|
||||
html_message = render_to_string('myadmin/mail_template.html', context)
|
||||
email_list = User.objects.filter(subscribe=True).values_list('email',flat=True)
|
||||
# 分组
|
||||
email_list = [email_list[i:i + 2] for i in range(0, len(email_list), 2)]
|
||||
|
||||
if email_list:
|
||||
for to_list in email_list:
|
||||
try:
|
||||
send_html_email(subject, html_message, to_list)
|
||||
except smtplib.SMTPException as e:
|
||||
logger.error(e)
|
||||
return JsonResponse({"code": 1, "msg": "发送失败"})
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
else:
|
||||
return JsonResponse({"code": 1, "msg": "邮件列表为空"})
|
||||
|
||||
|
||||
class FeedbackListView(AdminUserRequiredMixin, generic.ListView):
|
||||
model = Feedback
|
||||
template_name = 'myadmin/feedback_list.html'
|
||||
context_object_name = 'feedback_list'
|
||||
paginate_by = 10
|
||||
q = ''
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context = super(FeedbackListView, self).get_context_data(**kwargs)
|
||||
paginator = context.get('paginator')
|
||||
page = context.get('page_obj')
|
||||
page_list = get_page_list(paginator, page)
|
||||
context['page_list'] = page_list
|
||||
context['q'] = self.q
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
self.q = self.request.GET.get("q", "")
|
||||
return Feedback.objects.filter(content__contains=self.q).order_by('-timestamp')
|
||||
|
||||
|
||||
@ajax_required
|
||||
@require_http_methods(["POST"])
|
||||
def feedback_delete(request):
|
||||
if not request.user.is_superuser:
|
||||
return JsonResponse({"code": 1, "msg": "无删除权限"})
|
||||
feedback_id = request.POST['feedback_id']
|
||||
instance = Feedback.objects.get(id=feedback_id)
|
||||
instance.delete()
|
||||
return JsonResponse({"code": 0, "msg": "success"})
|
||||
|
@ -0,0 +1,6 @@
|
||||
django-ratelimit==1.0.1
|
||||
PyMySQL==1.0.2
|
||||
django_chunked_upload==1.1.3
|
||||
Django==3.2.11
|
||||
sorl-thumbnail==12.8.0
|
||||
pillow==9.1.1
|
@ -0,0 +1,95 @@
|
||||
body {
|
||||
display: relative;
|
||||
}
|
||||
|
||||
.n{
|
||||
display:none;
|
||||
}
|
||||
|
||||
#logo{
|
||||
margin-right: 16px;
|
||||
}
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
top: 51.8px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 18%;
|
||||
background-color: #f5f5f5;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#sidebar .ui.menu {
|
||||
margin: 2em 0 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#sidebar .ui.menu > a.item {
|
||||
color: #337ab7;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#sidebar .ui.menu > a.item.active {
|
||||
background-color: #337ab7;
|
||||
color: white;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
#sidebar .ui.menu > a.item:hover {
|
||||
background-color: #4f93ce;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 19%;
|
||||
width: 81%;
|
||||
margin-top: 3em;
|
||||
padding-left: 3em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#content > .ui.grid {
|
||||
padding-right: 4em;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
#content h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
#content .ui.divider:not(.hidden) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#content table.ui.table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#content table.ui.table thead th {
|
||||
border-bottom: 2px solid #eee !important;
|
||||
}
|
||||
|
||||
.v-title-extra{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.v-admin-search{
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#progress_layout{
|
||||
margin-top:2rem;
|
||||
}
|
||||
|
||||
.v-form-wrap {
|
||||
width: 600px;
|
||||
}
|
||||
.v-form-wrap .form{
|
||||
margin:2rem auto;
|
||||
}
|
||||
|
||||
.v-form-field{
|
||||
padding-top:1em;
|
||||
padding-bottom:1em;
|
||||
vertical-align:middle;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
.dropload-up,.dropload-down{
|
||||
position: relative;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
/* 开启硬件加速 */
|
||||
-webkit-transform:translateZ(0);
|
||||
transform:translateZ(0);
|
||||
}
|
||||
.dropload-down{
|
||||
height: 50px;
|
||||
}
|
||||
.dropload-refresh,.dropload-update,.dropload-load,.dropload-noData{
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
.dropload-load .loading{
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
border-radius: 100%;
|
||||
margin: 6px;
|
||||
border: 2px solid #666;
|
||||
border-bottom-color: transparent;
|
||||
vertical-align: middle;
|
||||
-webkit-animation: rotate 0.75s linear infinite;
|
||||
animation: rotate 0.75s linear infinite;
|
||||
}
|
||||
@-webkit-keyframes rotate {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
-webkit-transform: rotate(180deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,208 @@
|
||||
|
||||
body {
|
||||
margin:0px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.color-black{
|
||||
color:#000000;
|
||||
}
|
||||
|
||||
.cursor{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.n{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.fl{
|
||||
float:left;
|
||||
}
|
||||
.fr{
|
||||
float:right;
|
||||
}
|
||||
|
||||
.v-inline-middle{
|
||||
display:inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#v-content{
|
||||
padding-top:5.2em;
|
||||
}
|
||||
|
||||
#v-account-body{
|
||||
background-color: #DADADA;
|
||||
}
|
||||
#v-account-body > .grid{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ui.menu .item img.logo {
|
||||
margin-right: 0.66rem;
|
||||
}
|
||||
|
||||
.ui.footer.segment {
|
||||
margin: 2em 0em 0em;
|
||||
padding: 1em 0em;
|
||||
}
|
||||
|
||||
.v-account {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.video{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.v-video-search{
|
||||
margin-right: 3em;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.v-video-search i{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.v-container-top{
|
||||
height: 5.66em;
|
||||
}
|
||||
|
||||
.v-header-extra{
|
||||
position: relative;
|
||||
width:100%;
|
||||
text-align: right;
|
||||
padding-top: 0.60rem;
|
||||
}
|
||||
|
||||
.v-play-icon{
|
||||
position: absolute;
|
||||
left: 1.0rem;
|
||||
bottom: 1.0rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ui .card .image{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.v-icon{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.video-duration{
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header-title{
|
||||
margin-top: 30px;
|
||||
font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 1.28571429em;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.video-title{
|
||||
color: var(--yt-primary-text-color);
|
||||
font-size: 1.28em;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
-ms-flex: 1 1 0.000000001px;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.video-side-title{
|
||||
color: var(--yt-primary-text-color);
|
||||
font-size: 1.28em;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
-ms-flex: 1 1 0.000000001px;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.video-view-count{
|
||||
margin-top: 16px;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.video-view-operation{
|
||||
margin-top: 16px;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.video-info{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.video-page{
|
||||
display: block;
|
||||
margin:1.28em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.v-settings{
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.v-settings-side{
|
||||
border-right: 1px solid #e3e3e3;
|
||||
}
|
||||
|
||||
.v-settings-content .form{
|
||||
margin:0em auto;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.v-settings-nav{
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
width: 185px;
|
||||
min-height: 350px;
|
||||
margin-right: 100px;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.v-settings-avatar{
|
||||
display:inline-block;
|
||||
float:left;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.v-nav-title{
|
||||
margin-left:1.0em;
|
||||
}
|
||||
|
||||
.v-form-field{
|
||||
padding-top:1em;
|
||||
padding-bottom:1em;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.v-form-fix-avatar{
|
||||
padding:2em;
|
||||
}
|
||||
|
||||
.v-header-avatar{
|
||||
line-height:34px;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
.del {
|
||||
width:100px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
right: 40px;
|
||||
}
|
After Width: | Height: | Size: 702 KiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 222 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,32 @@
|
||||
function getCookie(name) {
|
||||
// Function to get any cookie available in the session.
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
};
|
||||
|
||||
function csrfSafeMethod(method) {
|
||||
// These HTTP methods do not require CSRF protection
|
||||
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
||||
}
|
||||
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
var page_title = $(document).attr("title");
|
||||
// This sets up every ajax call with proper headers.
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(xhr, settings) {
|
||||
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
||||
xhr.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
}
|
||||
}
|
||||
});
|
@ -0,0 +1,108 @@
|
||||
$(function () {
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
|
||||
// 点赞
|
||||
$("#like").click(function(){
|
||||
var video_id = $("#like").attr("video-id");
|
||||
$.ajax({
|
||||
url: '/video/like/',
|
||||
data: {
|
||||
video_id: video_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
var code = data.code
|
||||
if(code == 0){
|
||||
var likes = data.likes
|
||||
var user_liked = data.user_liked
|
||||
$('#like-count').text(likes)
|
||||
if(user_liked == 0){
|
||||
$('#like').removeClass("grey").addClass("red")
|
||||
}else{
|
||||
$('#like').removeClass("red").addClass("grey")
|
||||
}
|
||||
}else{
|
||||
var msg = data.msg
|
||||
alert(msg)
|
||||
}
|
||||
|
||||
},
|
||||
error: function(data){
|
||||
alert("点赞失败")
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 收藏
|
||||
$("#star").click(function(){
|
||||
var video_id = $("#star").attr("video-id");
|
||||
$.ajax({
|
||||
url: '/video/collect/',
|
||||
data: {
|
||||
video_id: video_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
var code = data.code
|
||||
if(code == 0){
|
||||
var collects = data.collects
|
||||
var user_collected = data.user_collected
|
||||
$('#collect-count').text(collects)
|
||||
if(user_collected == 0){
|
||||
$('#star').removeClass("grey").addClass("red")
|
||||
}else{
|
||||
$('#star').removeClass("red").addClass("grey")
|
||||
}
|
||||
}else{
|
||||
var msg = data.msg
|
||||
alert(msg)
|
||||
}
|
||||
|
||||
},
|
||||
error: function(data){
|
||||
alert("收藏失败")
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 提交评论
|
||||
var frm = $('#comment_form')
|
||||
frm.submit(function () {
|
||||
$.ajax({
|
||||
type: frm.attr('method'),
|
||||
url: frm.attr('action'),
|
||||
dataType:'json',
|
||||
data: frm.serialize(),
|
||||
success: function (data) {
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
$('#id_content').val("")
|
||||
$('.comment-list').prepend(data.html);
|
||||
$('#comment-result').text("评论成功")
|
||||
$('.info').show().delay(2000).fadeOut(800)
|
||||
}else{
|
||||
$('#comment-result').text(msg)
|
||||
$('.info').show().delay(2000).fadeOut(800);
|
||||
}
|
||||
},
|
||||
error: function(data) {
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
$(function(){
|
||||
|
||||
// 头像dropdown
|
||||
$('#v-header-avatar').dropdown();
|
||||
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
var explorerName = getExploreName();
|
||||
if(explorerName.startsWith("Safari")||explorerName.startsWith("IE")){
|
||||
alert("请使用Chrome浏览器访问该网站");
|
||||
}
|
||||
|
||||
function getExploreName(){
|
||||
var userAgent = navigator.userAgent;
|
||||
if(userAgent.indexOf("Opera") > -1 || userAgent.indexOf("OPR") > -1){
|
||||
return 'Opera';
|
||||
}else if(userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1){
|
||||
return 'IE';
|
||||
}else if(userAgent.indexOf("Edge") > -1){
|
||||
return 'Edge';
|
||||
}else if(userAgent.indexOf("Firefox") > -1){
|
||||
return 'Firefox';
|
||||
}else if(userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1){
|
||||
return 'Safari';
|
||||
}else if(userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1){
|
||||
return 'Chrome';
|
||||
}else if(!!window.ActiveXObject || "ActiveXObject" in window){
|
||||
return 'IE>=11';
|
||||
}else{
|
||||
return 'Unkonwn';
|
||||
}
|
||||
}
|
||||
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
$(function(){
|
||||
var pathname = window.location.pathname;
|
||||
console.log(pathname);
|
||||
if(pathname.indexOf("users/profile/") >= 0 ) {
|
||||
$("#id_profile").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("users/subscribe/") >= 0 ) {
|
||||
$("#id_subscribe").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("users/change_password/") >= 0 ) {
|
||||
$("#id_password").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("users/feedback/") >= 0 ) {
|
||||
$("#id_feedback").addClass("active");
|
||||
}
|
||||
});
|
@ -0,0 +1,42 @@
|
||||
|
||||
$(function(){
|
||||
// 页数
|
||||
var page = 0;
|
||||
// 每页展示15个
|
||||
var page_size = 15;
|
||||
|
||||
// dropload
|
||||
$('.comments').dropload({
|
||||
scrollArea : window,
|
||||
loadDownFn : function(me){
|
||||
page++;
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: comments_url,
|
||||
data:{
|
||||
video_id: video_id,
|
||||
page: page,
|
||||
page_size: page_size
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(data){
|
||||
var code = data.code
|
||||
var count = data.comment_count
|
||||
if(code == 0){
|
||||
$('#id_comment_label').text(count + "条评论");
|
||||
$('.comment-list').append(data.html);
|
||||
me.resetload();
|
||||
}else{
|
||||
me.lock();
|
||||
me.noData();
|
||||
me.resetload();
|
||||
}
|
||||
},
|
||||
error: function(xhr, type){
|
||||
me.resetload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
@ -0,0 +1,36 @@
|
||||
//$(document)
|
||||
// .ready(function() {
|
||||
// $('.ui.form')
|
||||
// .form({
|
||||
// fields: {
|
||||
// username: {
|
||||
// identifier : 'username',
|
||||
// rules: [
|
||||
// {
|
||||
// type : 'empty',
|
||||
// prompt : '请输入用户名'
|
||||
// },
|
||||
// {
|
||||
// type : 'empty',
|
||||
// prompt : '用户名不能为空'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// password: {
|
||||
// identifier : 'password',
|
||||
// rules: [
|
||||
// {
|
||||
// type : 'empty',
|
||||
// prompt : '请输入密码'
|
||||
// },
|
||||
// {
|
||||
// type : 'length[6]',
|
||||
// prompt : '密码必须六位数以上'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// ;
|
||||
// })
|
||||
// ;
|
@ -0,0 +1,46 @@
|
||||
$(function(){
|
||||
var pathname = window.location.pathname;
|
||||
console.log(pathname);
|
||||
if(pathname.endsWith("myadmin/")) {
|
||||
$("#index").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/video_list/")) {
|
||||
$("#video_list").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/video_edit/") >= 0){
|
||||
$("#video_list").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/video_add/")) {
|
||||
$("#video_add").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/classification_list/")) {
|
||||
$("#classification_list").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/classification_edit/") >= 0){
|
||||
$("#classification_list").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/classification_add/")) {
|
||||
$("#classification_add").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/user_list/")) {
|
||||
$("#user_list").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/user_edit/") >= 0){
|
||||
$("#user_list").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/user_add/")) {
|
||||
$("#user_add").addClass("active");
|
||||
}
|
||||
if(pathname.endsWith("myadmin/comment_list/")) {
|
||||
$("#comment_list").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/setting/") >= 0) {
|
||||
$("#setting").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/subscribe/") >= 0){
|
||||
$("#subscribe").addClass("active");
|
||||
}
|
||||
if(pathname.indexOf("myadmin/feedback_list/") >= 0){
|
||||
$("#feedback_list").addClass("active");
|
||||
}
|
||||
});
|
@ -0,0 +1,59 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
$('.classification-delete').click(function(){
|
||||
var tr = $(this).closest("tr");
|
||||
var classification_id = $(tr).attr("classification-id");
|
||||
$('.ui.tiny.modal.delete')
|
||||
.modal({
|
||||
closable : true,
|
||||
onDeny : function(){
|
||||
return true;
|
||||
},
|
||||
onApprove : function() {
|
||||
|
||||
$.ajax({
|
||||
url: api_classification_delete,
|
||||
data: {
|
||||
'classification_id':classification_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
window.location.reload();
|
||||
}else{
|
||||
alert(msg);
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
alert("error"+data)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
|
||||
// search
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
})
|
@ -0,0 +1,59 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
$('.comment-delete').click(function(){
|
||||
var tr = $(this).closest("tr");
|
||||
var comment_id = $(tr).attr("comment-id");
|
||||
$('.ui.tiny.modal.delete')
|
||||
.modal({
|
||||
closable : true,
|
||||
onDeny : function(){
|
||||
return true;
|
||||
},
|
||||
onApprove : function() {
|
||||
|
||||
$.ajax({
|
||||
url: api_comment_delete,
|
||||
data: {
|
||||
'comment_id':comment_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
window.location.reload();
|
||||
}else{
|
||||
alert(msg);
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
alert("error"+data)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
|
||||
// search
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
})
|
@ -0,0 +1,59 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
$('.feedback-delete').click(function(){
|
||||
var tr = $(this).closest("tr");
|
||||
var feedback_id = $(tr).attr("feedback-id");
|
||||
$('.ui.tiny.modal.delete')
|
||||
.modal({
|
||||
closable : true,
|
||||
onDeny : function(){
|
||||
return true;
|
||||
},
|
||||
onApprove : function() {
|
||||
|
||||
$.ajax({
|
||||
url: api_feedback_delete,
|
||||
data: {
|
||||
'feedback_id':feedback_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
window.location.reload();
|
||||
}else{
|
||||
alert(msg);
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
alert("error"+data)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
|
||||
// search
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
})
|
@ -0,0 +1,37 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
$('#send-mail').click(function(){
|
||||
|
||||
var video_id = $('.selection.dropdown').dropdown('get value');
|
||||
|
||||
if(video_id == ''){
|
||||
alert("不能为空");
|
||||
return;
|
||||
}
|
||||
$('#send-mail-progress').text('正在发送通知...')
|
||||
$.ajax({
|
||||
url: api_send_mail,
|
||||
data: {
|
||||
'video_id':video_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
$('#send-mail-progress').text('发送成功')
|
||||
}else{
|
||||
$('#send-mail-progress').text(msg)
|
||||
}
|
||||
|
||||
},
|
||||
error: function(data){
|
||||
$('#send-mail-progress').text('发送失败')
|
||||
}
|
||||
});
|
||||
});
|
@ -0,0 +1,58 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
$('.user-delete').click(function(){
|
||||
var tr = $(this).closest("tr");
|
||||
var user_id = $(tr).attr("user-id");
|
||||
$('.ui.tiny.modal.delete')
|
||||
.modal({
|
||||
closable : true,
|
||||
onDeny : function(){
|
||||
return true;
|
||||
},
|
||||
onApprove : function() {
|
||||
|
||||
$.ajax({
|
||||
url: api_user_delete,
|
||||
data: {
|
||||
'user_id':user_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
if(code == 0){
|
||||
window.location.reload();
|
||||
}else{
|
||||
alert(""+data.msg)
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
alert("error"+data)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
|
||||
// search
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
})
|
@ -0,0 +1,60 @@
|
||||
|
||||
// 写入csrf
|
||||
$.getScript("/static/js/csrftoken.js");
|
||||
|
||||
// 删除
|
||||
$(".video-list").on("click", ".video-delete", function () {
|
||||
var tr = $(this).closest("tr");
|
||||
var video_id = $(tr).attr("video-id");
|
||||
$('.ui.tiny.modal.delete')
|
||||
.modal({
|
||||
closable : true,
|
||||
onDeny : function(){
|
||||
return true;
|
||||
},
|
||||
onApprove : function() {
|
||||
|
||||
$.ajax({
|
||||
url: api_video_delete,
|
||||
data: {
|
||||
'video_id':video_id,
|
||||
'csrf_token': csrftoken
|
||||
},
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
console.log(data);
|
||||
var code = data.code
|
||||
var msg = data.msg
|
||||
if(code == 0){
|
||||
window.location.reload();
|
||||
}else{
|
||||
alert(msg)
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
alert("error"+data)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
.modal('show');
|
||||
});
|
||||
|
||||
|
||||
// search
|
||||
$('#v-search').bind('keypress',function(event){
|
||||
var word = $('#v-search').val()
|
||||
if(event.keyCode == "13" && word.length > 0)
|
||||
{
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
});
|
||||
|
||||
$('#search').click(function(){
|
||||
var word = $('#v-search').val()
|
||||
if(word.length > 0){
|
||||
window.location = search_url + '?q='+word;
|
||||
}
|
||||
})
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* jQuery Iframe Transport Plugin 1.8.3
|
||||
* https://github.com/blueimp/jQuery-File-Upload
|
||||
*
|
||||
* Copyright 2011, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* global define, require, window, document */
|
||||
|
||||
(function (factory) {
|
||||
'use strict';
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// Register as an anonymous AMD module:
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node/CommonJS:
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals:
|
||||
factory(window.jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
'use strict';
|
||||
|
||||
// Helper variable to create unique names for the transport iframes:
|
||||
var counter = 0;
|
||||
|
||||
// The iframe transport accepts four additional options:
|
||||
// options.fileInput: a jQuery collection of file input fields
|
||||
// options.paramName: the parameter name for the file form data,
|
||||
// overrides the name property of the file input field(s),
|
||||
// can be a string or an array of strings.
|
||||
// options.formData: an array of objects with name and value properties,
|
||||
// equivalent to the return data of .serializeArray(), e.g.:
|
||||
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
|
||||
// options.initialIframeSrc: the URL of the initial iframe src,
|
||||
// by default set to "javascript:false;"
|
||||
$.ajaxTransport('iframe', function (options) {
|
||||
if (options.async) {
|
||||
// javascript:false as initial iframe src
|
||||
// prevents warning popups on HTTPS in IE6:
|
||||
/*jshint scripturl: true */
|
||||
var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
|
||||
/*jshint scripturl: false */
|
||||
form,
|
||||
iframe,
|
||||
addParamChar;
|
||||
return {
|
||||
send: function (_, completeCallback) {
|
||||
form = $('<form style="display:none;"></form>');
|
||||
form.attr('accept-charset', options.formAcceptCharset);
|
||||
addParamChar = /\?/.test(options.url) ? '&' : '?';
|
||||
// XDomainRequest only supports GET and POST:
|
||||
if (options.type === 'DELETE') {
|
||||
options.url = options.url + addParamChar + '_method=DELETE';
|
||||
options.type = 'POST';
|
||||
} else if (options.type === 'PUT') {
|
||||
options.url = options.url + addParamChar + '_method=PUT';
|
||||
options.type = 'POST';
|
||||
} else if (options.type === 'PATCH') {
|
||||
options.url = options.url + addParamChar + '_method=PATCH';
|
||||
options.type = 'POST';
|
||||
}
|
||||
// IE versions below IE8 cannot set the name property of
|
||||
// elements that have already been added to the DOM,
|
||||
// so we set the name along with the iframe HTML markup:
|
||||
counter += 1;
|
||||
iframe = $(
|
||||
'<iframe src="' + initialIframeSrc +
|
||||
'" name="iframe-transport-' + counter + '"></iframe>'
|
||||
).bind('load', function () {
|
||||
var fileInputClones,
|
||||
paramNames = $.isArray(options.paramName) ?
|
||||
options.paramName : [options.paramName];
|
||||
iframe
|
||||
.unbind('load')
|
||||
.bind('load', function () {
|
||||
var response;
|
||||
// Wrap in a try/catch block to catch exceptions thrown
|
||||
// when trying to access cross-domain iframe contents:
|
||||
try {
|
||||
response = iframe.contents();
|
||||
// Google Chrome and Firefox do not throw an
|
||||
// exception when calling iframe.contents() on
|
||||
// cross-domain requests, so we unify the response:
|
||||
if (!response.length || !response[0].firstChild) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (e) {
|
||||
response = undefined;
|
||||
}
|
||||
// The complete callback returns the
|
||||
// iframe content document as response object:
|
||||
completeCallback(
|
||||
200,
|
||||
'success',
|
||||
{'iframe': response}
|
||||
);
|
||||
// Fix for IE endless progress bar activity bug
|
||||
// (happens on form submits to iframe targets):
|
||||
$('<iframe src="' + initialIframeSrc + '"></iframe>')
|
||||
.appendTo(form);
|
||||
window.setTimeout(function () {
|
||||
// Removing the form in a setTimeout call
|
||||
// allows Chrome's developer tools to display
|
||||
// the response result
|
||||
form.remove();
|
||||
}, 0);
|
||||
});
|
||||
form
|
||||
.prop('target', iframe.prop('name'))
|
||||
.prop('action', options.url)
|
||||
.prop('method', options.type);
|
||||
if (options.formData) {
|
||||
$.each(options.formData, function (index, field) {
|
||||
$('<input type="hidden"/>')
|
||||
.prop('name', field.name)
|
||||
.val(field.value)
|
||||
.appendTo(form);
|
||||
});
|
||||
}
|
||||
if (options.fileInput && options.fileInput.length &&
|
||||
options.type === 'POST') {
|
||||
fileInputClones = options.fileInput.clone();
|
||||
// Insert a clone for each file input field:
|
||||
options.fileInput.after(function (index) {
|
||||
return fileInputClones[index];
|
||||
});
|
||||
if (options.paramName) {
|
||||
options.fileInput.each(function (index) {
|
||||
$(this).prop(
|
||||
'name',
|
||||
paramNames[index] || options.paramName
|
||||
);
|
||||
});
|
||||
}
|
||||
// Appending the file input fields to the hidden form
|
||||
// removes them from their original location:
|
||||
form
|
||||
.append(options.fileInput)
|
||||
.prop('enctype', 'multipart/form-data')
|
||||
// enctype must be set as encoding for IE:
|
||||
.prop('encoding', 'multipart/form-data');
|
||||
// Remove the HTML5 form attribute from the input(s):
|
||||
options.fileInput.removeAttr('form');
|
||||
}
|
||||
form.submit();
|
||||
// Insert the file input fields at their original location
|
||||
// by replacing the clones with the originals:
|
||||
if (fileInputClones && fileInputClones.length) {
|
||||
options.fileInput.each(function (index, input) {
|
||||
var clone = $(fileInputClones[index]);
|
||||
// Restore the original name and form properties:
|
||||
$(input)
|
||||
.prop('name', clone.prop('name'))
|
||||
.attr('form', clone.attr('form'));
|
||||
clone.replaceWith(input);
|
||||
});
|
||||
}
|
||||
});
|
||||
form.append(iframe).appendTo(document.body);
|
||||
},
|
||||
abort: function () {
|
||||
if (iframe) {
|
||||
// javascript:false as iframe src aborts the request
|
||||
// and prevents warning popups on HTTPS in IE6.
|
||||
// concat is used to avoid the "Script URL" JSLint error:
|
||||
iframe
|
||||
.unbind('load')
|
||||
.prop('src', initialIframeSrc);
|
||||
}
|
||||
if (form) {
|
||||
form.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// The iframe transport returns the iframe content document as response.
|
||||
// The following adds converters from iframe to text, json, html, xml
|
||||
// and script.
|
||||
// Please note that the Content-Type for JSON responses has to be text/plain
|
||||
// or text/html, if the browser doesn't include application/json in the
|
||||
// Accept header, else IE will show a download dialog.
|
||||
// The Content-Type for XML responses on the other hand has to be always
|
||||
// application/xml or text/xml, so IE properly parses the XML response.
|
||||
// See also
|
||||
// https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
|
||||
$.ajaxSetup({
|
||||
converters: {
|
||||
'iframe text': function (iframe) {
|
||||
return iframe && $(iframe[0].body).text();
|
||||
},
|
||||
'iframe json': function (iframe) {
|
||||
return iframe && $.parseJSON($(iframe[0].body).text());
|
||||
},
|
||||
'iframe html': function (iframe) {
|
||||
return iframe && $(iframe[0].body).html();
|
||||
},
|
||||
'iframe xml': function (iframe) {
|
||||
var xmlDoc = iframe && iframe[0];
|
||||
return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
|
||||
$.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
|
||||
$(xmlDoc.body).html());
|
||||
},
|
||||
'iframe script': function (iframe) {
|
||||
return iframe && $.globalEval($(iframe[0].body).text());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}));
|
@ -0,0 +1,563 @@
|
||||
/*! jQuery UI - v1.11.1+CommonJS - 2014-09-17
|
||||
* http://jqueryui.com
|
||||
* Includes: widget.js
|
||||
* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
|
||||
|
||||
(function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define([ "jquery" ], factory );
|
||||
|
||||
} else if (typeof exports === "object") {
|
||||
// Node/CommonJS:
|
||||
factory(require("jquery"));
|
||||
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery );
|
||||
}
|
||||
}(function( $ ) {
|
||||
/*!
|
||||
* jQuery UI Widget 1.11.1
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright 2014 jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/jQuery.widget/
|
||||
*/
|
||||
|
||||
|
||||
var widget_uuid = 0,
|
||||
widget_slice = Array.prototype.slice;
|
||||
|
||||
$.cleanData = (function( orig ) {
|
||||
return function( elems ) {
|
||||
var events, elem, i;
|
||||
for ( i = 0; (elem = elems[i]) != null; i++ ) {
|
||||
try {
|
||||
|
||||
// Only trigger remove when necessary to save time
|
||||
events = $._data( elem, "events" );
|
||||
if ( events && events.remove ) {
|
||||
$( elem ).triggerHandler( "remove" );
|
||||
}
|
||||
|
||||
// http://bugs.jquery.com/ticket/8235
|
||||
} catch( e ) {}
|
||||
}
|
||||
orig( elems );
|
||||
};
|
||||
})( $.cleanData );
|
||||
|
||||
$.widget = function( name, base, prototype ) {
|
||||
var fullName, existingConstructor, constructor, basePrototype,
|
||||
// proxiedPrototype allows the provided prototype to remain unmodified
|
||||
// so that it can be used as a mixin for multiple widgets (#8876)
|
||||
proxiedPrototype = {},
|
||||
namespace = name.split( "." )[ 0 ];
|
||||
|
||||
name = name.split( "." )[ 1 ];
|
||||
fullName = namespace + "-" + name;
|
||||
|
||||
if ( !prototype ) {
|
||||
prototype = base;
|
||||
base = $.Widget;
|
||||
}
|
||||
|
||||
// create selector for plugin
|
||||
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
|
||||
return !!$.data( elem, fullName );
|
||||
};
|
||||
|
||||
$[ namespace ] = $[ namespace ] || {};
|
||||
existingConstructor = $[ namespace ][ name ];
|
||||
constructor = $[ namespace ][ name ] = function( options, element ) {
|
||||
// allow instantiation without "new" keyword
|
||||
if ( !this._createWidget ) {
|
||||
return new constructor( options, element );
|
||||
}
|
||||
|
||||
// allow instantiation without initializing for simple inheritance
|
||||
// must use "new" keyword (the code above always passes args)
|
||||
if ( arguments.length ) {
|
||||
this._createWidget( options, element );
|
||||
}
|
||||
};
|
||||
// extend with the existing constructor to carry over any static properties
|
||||
$.extend( constructor, existingConstructor, {
|
||||
version: prototype.version,
|
||||
// copy the object used to create the prototype in case we need to
|
||||
// redefine the widget later
|
||||
_proto: $.extend( {}, prototype ),
|
||||
// track widgets that inherit from this widget in case this widget is
|
||||
// redefined after a widget inherits from it
|
||||
_childConstructors: []
|
||||
});
|
||||
|
||||
basePrototype = new base();
|
||||
// we need to make the options hash a property directly on the new instance
|
||||
// otherwise we'll modify the options hash on the prototype that we're
|
||||
// inheriting from
|
||||
basePrototype.options = $.widget.extend( {}, basePrototype.options );
|
||||
$.each( prototype, function( prop, value ) {
|
||||
if ( !$.isFunction( value ) ) {
|
||||
proxiedPrototype[ prop ] = value;
|
||||
return;
|
||||
}
|
||||
proxiedPrototype[ prop ] = (function() {
|
||||
var _super = function() {
|
||||
return base.prototype[ prop ].apply( this, arguments );
|
||||
},
|
||||
_superApply = function( args ) {
|
||||
return base.prototype[ prop ].apply( this, args );
|
||||
};
|
||||
return function() {
|
||||
var __super = this._super,
|
||||
__superApply = this._superApply,
|
||||
returnValue;
|
||||
|
||||
this._super = _super;
|
||||
this._superApply = _superApply;
|
||||
|
||||
returnValue = value.apply( this, arguments );
|
||||
|
||||
this._super = __super;
|
||||
this._superApply = __superApply;
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
})();
|
||||
});
|
||||
constructor.prototype = $.widget.extend( basePrototype, {
|
||||
// TODO: remove support for widgetEventPrefix
|
||||
// always use the name + a colon as the prefix, e.g., draggable:start
|
||||
// don't prefix for widgets that aren't DOM-based
|
||||
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
|
||||
}, proxiedPrototype, {
|
||||
constructor: constructor,
|
||||
namespace: namespace,
|
||||
widgetName: name,
|
||||
widgetFullName: fullName
|
||||
});
|
||||
|
||||
// If this widget is being redefined then we need to find all widgets that
|
||||
// are inheriting from it and redefine all of them so that they inherit from
|
||||
// the new version of this widget. We're essentially trying to replace one
|
||||
// level in the prototype chain.
|
||||
if ( existingConstructor ) {
|
||||
$.each( existingConstructor._childConstructors, function( i, child ) {
|
||||
var childPrototype = child.prototype;
|
||||
|
||||
// redefine the child widget using the same prototype that was
|
||||
// originally used, but inherit from the new version of the base
|
||||
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
|
||||
});
|
||||
// remove the list of existing child constructors from the old constructor
|
||||
// so the old child constructors can be garbage collected
|
||||
delete existingConstructor._childConstructors;
|
||||
} else {
|
||||
base._childConstructors.push( constructor );
|
||||
}
|
||||
|
||||
$.widget.bridge( name, constructor );
|
||||
|
||||
return constructor;
|
||||
};
|
||||
|
||||
$.widget.extend = function( target ) {
|
||||
var input = widget_slice.call( arguments, 1 ),
|
||||
inputIndex = 0,
|
||||
inputLength = input.length,
|
||||
key,
|
||||
value;
|
||||
for ( ; inputIndex < inputLength; inputIndex++ ) {
|
||||
for ( key in input[ inputIndex ] ) {
|
||||
value = input[ inputIndex ][ key ];
|
||||
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
|
||||
// Clone objects
|
||||
if ( $.isPlainObject( value ) ) {
|
||||
target[ key ] = $.isPlainObject( target[ key ] ) ?
|
||||
$.widget.extend( {}, target[ key ], value ) :
|
||||
// Don't extend strings, arrays, etc. with objects
|
||||
$.widget.extend( {}, value );
|
||||
// Copy everything else by reference
|
||||
} else {
|
||||
target[ key ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
$.widget.bridge = function( name, object ) {
|
||||
var fullName = object.prototype.widgetFullName || name;
|
||||
$.fn[ name ] = function( options ) {
|
||||
var isMethodCall = typeof options === "string",
|
||||
args = widget_slice.call( arguments, 1 ),
|
||||
returnValue = this;
|
||||
|
||||
// allow multiple hashes to be passed on init
|
||||
options = !isMethodCall && args.length ?
|
||||
$.widget.extend.apply( null, [ options ].concat(args) ) :
|
||||
options;
|
||||
|
||||
if ( isMethodCall ) {
|
||||
this.each(function() {
|
||||
var methodValue,
|
||||
instance = $.data( this, fullName );
|
||||
if ( options === "instance" ) {
|
||||
returnValue = instance;
|
||||
return false;
|
||||
}
|
||||
if ( !instance ) {
|
||||
return $.error( "cannot call methods on " + name + " prior to initialization; " +
|
||||
"attempted to call method '" + options + "'" );
|
||||
}
|
||||
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
|
||||
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
|
||||
}
|
||||
methodValue = instance[ options ].apply( instance, args );
|
||||
if ( methodValue !== instance && methodValue !== undefined ) {
|
||||
returnValue = methodValue && methodValue.jquery ?
|
||||
returnValue.pushStack( methodValue.get() ) :
|
||||
methodValue;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.each(function() {
|
||||
var instance = $.data( this, fullName );
|
||||
if ( instance ) {
|
||||
instance.option( options || {} );
|
||||
if ( instance._init ) {
|
||||
instance._init();
|
||||
}
|
||||
} else {
|
||||
$.data( this, fullName, new object( options, this ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
};
|
||||
};
|
||||
|
||||
$.Widget = function( /* options, element */ ) {};
|
||||
$.Widget._childConstructors = [];
|
||||
|
||||
$.Widget.prototype = {
|
||||
widgetName: "widget",
|
||||
widgetEventPrefix: "",
|
||||
defaultElement: "<div>",
|
||||
options: {
|
||||
disabled: false,
|
||||
|
||||
// callbacks
|
||||
create: null
|
||||
},
|
||||
_createWidget: function( options, element ) {
|
||||
element = $( element || this.defaultElement || this )[ 0 ];
|
||||
this.element = $( element );
|
||||
this.uuid = widget_uuid++;
|
||||
this.eventNamespace = "." + this.widgetName + this.uuid;
|
||||
this.options = $.widget.extend( {},
|
||||
this.options,
|
||||
this._getCreateOptions(),
|
||||
options );
|
||||
|
||||
this.bindings = $();
|
||||
this.hoverable = $();
|
||||
this.focusable = $();
|
||||
|
||||
if ( element !== this ) {
|
||||
$.data( element, this.widgetFullName, this );
|
||||
this._on( true, this.element, {
|
||||
remove: function( event ) {
|
||||
if ( event.target === element ) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.document = $( element.style ?
|
||||
// element within the document
|
||||
element.ownerDocument :
|
||||
// element is window or document
|
||||
element.document || element );
|
||||
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
|
||||
}
|
||||
|
||||
this._create();
|
||||
this._trigger( "create", null, this._getCreateEventData() );
|
||||
this._init();
|
||||
},
|
||||
_getCreateOptions: $.noop,
|
||||
_getCreateEventData: $.noop,
|
||||
_create: $.noop,
|
||||
_init: $.noop,
|
||||
|
||||
destroy: function() {
|
||||
this._destroy();
|
||||
// we can probably remove the unbind calls in 2.0
|
||||
// all event bindings should go through this._on()
|
||||
this.element
|
||||
.unbind( this.eventNamespace )
|
||||
.removeData( this.widgetFullName )
|
||||
// support: jquery <1.6.3
|
||||
// http://bugs.jquery.com/ticket/9413
|
||||
.removeData( $.camelCase( this.widgetFullName ) );
|
||||
this.widget()
|
||||
.unbind( this.eventNamespace )
|
||||
.removeAttr( "aria-disabled" )
|
||||
.removeClass(
|
||||
this.widgetFullName + "-disabled " +
|
||||
"ui-state-disabled" );
|
||||
|
||||
// clean up events and states
|
||||
this.bindings.unbind( this.eventNamespace );
|
||||
this.hoverable.removeClass( "ui-state-hover" );
|
||||
this.focusable.removeClass( "ui-state-focus" );
|
||||
},
|
||||
_destroy: $.noop,
|
||||
|
||||
widget: function() {
|
||||
return this.element;
|
||||
},
|
||||
|
||||
option: function( key, value ) {
|
||||
var options = key,
|
||||
parts,
|
||||
curOption,
|
||||
i;
|
||||
|
||||
if ( arguments.length === 0 ) {
|
||||
// don't return a reference to the internal hash
|
||||
return $.widget.extend( {}, this.options );
|
||||
}
|
||||
|
||||
if ( typeof key === "string" ) {
|
||||
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
|
||||
options = {};
|
||||
parts = key.split( "." );
|
||||
key = parts.shift();
|
||||
if ( parts.length ) {
|
||||
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
|
||||
for ( i = 0; i < parts.length - 1; i++ ) {
|
||||
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
|
||||
curOption = curOption[ parts[ i ] ];
|
||||
}
|
||||
key = parts.pop();
|
||||
if ( arguments.length === 1 ) {
|
||||
return curOption[ key ] === undefined ? null : curOption[ key ];
|
||||
}
|
||||
curOption[ key ] = value;
|
||||
} else {
|
||||
if ( arguments.length === 1 ) {
|
||||
return this.options[ key ] === undefined ? null : this.options[ key ];
|
||||
}
|
||||
options[ key ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this._setOptions( options );
|
||||
|
||||
return this;
|
||||
},
|
||||
_setOptions: function( options ) {
|
||||
var key;
|
||||
|
||||
for ( key in options ) {
|
||||
this._setOption( key, options[ key ] );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
_setOption: function( key, value ) {
|
||||
this.options[ key ] = value;
|
||||
|
||||
if ( key === "disabled" ) {
|
||||
this.widget()
|
||||
.toggleClass( this.widgetFullName + "-disabled", !!value );
|
||||
|
||||
// If the widget is becoming disabled, then nothing is interactive
|
||||
if ( value ) {
|
||||
this.hoverable.removeClass( "ui-state-hover" );
|
||||
this.focusable.removeClass( "ui-state-focus" );
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
return this._setOptions({ disabled: false });
|
||||
},
|
||||
disable: function() {
|
||||
return this._setOptions({ disabled: true });
|
||||
},
|
||||
|
||||
_on: function( suppressDisabledCheck, element, handlers ) {
|
||||
var delegateElement,
|
||||
instance = this;
|
||||
|
||||
// no suppressDisabledCheck flag, shuffle arguments
|
||||
if ( typeof suppressDisabledCheck !== "boolean" ) {
|
||||
handlers = element;
|
||||
element = suppressDisabledCheck;
|
||||
suppressDisabledCheck = false;
|
||||
}
|
||||
|
||||
// no element argument, shuffle and use this.element
|
||||
if ( !handlers ) {
|
||||
handlers = element;
|
||||
element = this.element;
|
||||
delegateElement = this.widget();
|
||||
} else {
|
||||
element = delegateElement = $( element );
|
||||
this.bindings = this.bindings.add( element );
|
||||
}
|
||||
|
||||
$.each( handlers, function( event, handler ) {
|
||||
function handlerProxy() {
|
||||
// allow widgets to customize the disabled handling
|
||||
// - disabled as an array instead of boolean
|
||||
// - disabled class as method for disabling individual parts
|
||||
if ( !suppressDisabledCheck &&
|
||||
( instance.options.disabled === true ||
|
||||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
|
||||
return;
|
||||
}
|
||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||||
.apply( instance, arguments );
|
||||
}
|
||||
|
||||
// copy the guid so direct unbinding works
|
||||
if ( typeof handler !== "string" ) {
|
||||
handlerProxy.guid = handler.guid =
|
||||
handler.guid || handlerProxy.guid || $.guid++;
|
||||
}
|
||||
|
||||
var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
|
||||
eventName = match[1] + instance.eventNamespace,
|
||||
selector = match[2];
|
||||
if ( selector ) {
|
||||
delegateElement.delegate( selector, eventName, handlerProxy );
|
||||
} else {
|
||||
element.bind( eventName, handlerProxy );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_off: function( element, eventName ) {
|
||||
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
|
||||
element.unbind( eventName ).undelegate( eventName );
|
||||
},
|
||||
|
||||
_delay: function( handler, delay ) {
|
||||
function handlerProxy() {
|
||||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||||
.apply( instance, arguments );
|
||||
}
|
||||
var instance = this;
|
||||
return setTimeout( handlerProxy, delay || 0 );
|
||||
},
|
||||
|
||||
_hoverable: function( element ) {
|
||||
this.hoverable = this.hoverable.add( element );
|
||||
this._on( element, {
|
||||
mouseenter: function( event ) {
|
||||
$( event.currentTarget ).addClass( "ui-state-hover" );
|
||||
},
|
||||
mouseleave: function( event ) {
|
||||
$( event.currentTarget ).removeClass( "ui-state-hover" );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_focusable: function( element ) {
|
||||
this.focusable = this.focusable.add( element );
|
||||
this._on( element, {
|
||||
focusin: function( event ) {
|
||||
$( event.currentTarget ).addClass( "ui-state-focus" );
|
||||
},
|
||||
focusout: function( event ) {
|
||||
$( event.currentTarget ).removeClass( "ui-state-focus" );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_trigger: function( type, event, data ) {
|
||||
var prop, orig,
|
||||
callback = this.options[ type ];
|
||||
|
||||
data = data || {};
|
||||
event = $.Event( event );
|
||||
event.type = ( type === this.widgetEventPrefix ?
|
||||
type :
|
||||
this.widgetEventPrefix + type ).toLowerCase();
|
||||
// the original event may come from any element
|
||||
// so we need to reset the target on the new event
|
||||
event.target = this.element[ 0 ];
|
||||
|
||||
// copy original event properties over to the new event
|
||||
orig = event.originalEvent;
|
||||
if ( orig ) {
|
||||
for ( prop in orig ) {
|
||||
if ( !( prop in event ) ) {
|
||||
event[ prop ] = orig[ prop ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.element.trigger( event, data );
|
||||
return !( $.isFunction( callback ) &&
|
||||
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
|
||||
event.isDefaultPrevented() );
|
||||
}
|
||||
};
|
||||
|
||||
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
|
||||
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
|
||||
if ( typeof options === "string" ) {
|
||||
options = { effect: options };
|
||||
}
|
||||
var hasOptions,
|
||||
effectName = !options ?
|
||||
method :
|
||||
options === true || typeof options === "number" ?
|
||||
defaultEffect :
|
||||
options.effect || defaultEffect;
|
||||
options = options || {};
|
||||
if ( typeof options === "number" ) {
|
||||
options = { duration: options };
|
||||
}
|
||||
hasOptions = !$.isEmptyObject( options );
|
||||
options.complete = callback;
|
||||
if ( options.delay ) {
|
||||
element.delay( options.delay );
|
||||
}
|
||||
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
|
||||
element[ method ]( options );
|
||||
} else if ( effectName !== method && element[ effectName ] ) {
|
||||
element[ effectName ]( options.duration, options.easing, callback );
|
||||
} else {
|
||||
element.queue(function( next ) {
|
||||
$( this )[ method ]();
|
||||
if ( callback ) {
|
||||
callback.call( element[ 0 ] );
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
var widget = $.widget;
|
||||
|
||||
|
||||
|
||||
}));
|
@ -0,0 +1,49 @@
|
||||
{% load static %}
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="description" content="基于Django的视频点播网站">
|
||||
<meta name="google-site-verification" content="UhrjUG70pwiY6Sp4u4p5Q3ol8iD7yx4ZSwmGBpoaWik" />
|
||||
<link rel="icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
|
||||
<title>视点</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/semantic.custom.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
|
||||
{% block css %}{% endblock css %}
|
||||
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?e3be3decd24d944e0bc592558617d210";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{% include "base/header.html" %}
|
||||
<div class="ui container" id="v-content">
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
</div>
|
||||
{% include "base/footer.html" %}
|
||||
|
||||
<script src="https://cdn.staticfile.org/jquery/1.9.0/jquery.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script>
|
||||
var search_url = "{% url 'video:search' %}"
|
||||
</script>
|
||||
<script src="{% static 'js/header.js' %}"></script>
|
||||
|
||||
{% block javascript %}
|
||||
{% endblock javascript %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,13 @@
|
||||
{% load static %}
|
||||
|
||||
<div class="ui vertical footer segment ">
|
||||
<div class="ui center aligned container">
|
||||
<div class="ui divider"></div>
|
||||
<img src="{% static 'img/logo.png' %}" class="ui centered mini image">
|
||||
<div class="ui horizontal small divided link list">
|
||||
<a class="item" href="#">北京堆栈科技有限公司</a>
|
||||
<a class="item" href="#">联系我们</a>
|
||||
<a class="item" href="#">京ICP证090287</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="ui info message">
|
||||
<ul class="list">
|
||||
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<li class="item">{{ error|escape }}</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="item">{{ error|escape }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
@ -0,0 +1,7 @@
|
||||
{% if messages %}
|
||||
<div class="ui info message">
|
||||
{% for message in messages %}
|
||||
<div class="item">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
@ -0,0 +1,52 @@
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
<div class="ui sticky fixed menu">
|
||||
<div class="ui container">
|
||||
<a href="/" class="borderless header item">
|
||||
<img class="logo" src="{% static 'img/logo.png' %}">
|
||||
视点
|
||||
</a>
|
||||
<div class="v-header-extra">
|
||||
<div class="ui small icon input v-video-search">
|
||||
<input class="prompt" value="{{ q }}" type="text" placeholder="搜索视频" id="v-search">
|
||||
<i id="search" class="search icon" style="cursor:pointer;"></i>
|
||||
</div>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<div class="ui inline dropdown" id="v-header-avatar" style="">
|
||||
<div class="" style="display:inline-block;font-weight:bold;">
|
||||
{% thumbnail user.avatar "200x200" crop="center" as im %}
|
||||
<img class="ui avatar image" src="{{ im.url }}">
|
||||
{% empty %}
|
||||
<img class="ui avatar image" src="{% static 'img/img_default_avatar.png' %}">
|
||||
{% endthumbnail %}
|
||||
{{ user.username }}
|
||||
</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" onclick="window.location='{% url 'users:profile' user.pk %}';">
|
||||
<i class="user icon"></i>
|
||||
<span>个人资料</span>
|
||||
</div>
|
||||
<div class="item" onclick="window.location='{% url 'users:collect_videos' user.pk %}';">
|
||||
<i class="bookmark icon"></i>
|
||||
<span>我的收藏</span>
|
||||
</div>
|
||||
<div class="item" onclick="window.location='{% url 'users:like_videos' user.pk %}';">
|
||||
<i class="heart icon"></i>
|
||||
<span>我的喜欢</span>
|
||||
</div>
|
||||
<div class="item" onclick="window.location='{% url 'users:logout' %}';">
|
||||
<i class="sign-out icon"></i>
|
||||
<span>退出</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a class="ui tiny secondary basic button" id="v-header-login" href="{% url 'users:login' %}?next={{ request.path }}">登录</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,18 @@
|
||||
<div class="ui secondary vertical pointing menu">
|
||||
<a id="id_profile" class="link item" href="{% url 'users:profile' user.pk %}">
|
||||
<i class="grey user icon"></i>
|
||||
<span class="v-nav-title">个人资料</span>
|
||||
</a>
|
||||
<a id="id_password" class="link item" href="{% url 'users:change_password' %}">
|
||||
<i class="grey edit icon"></i>
|
||||
<span class="v-nav-title">修改密码</span>
|
||||
</a>
|
||||
<a id="id_subscribe" class="link item" href="{% url 'users:subscribe' user.pk %}">
|
||||
<i class="grey rss icon"></i>
|
||||
<span class="v-nav-title">订阅设置</span>
|
||||
</a>
|
||||
<a id="id_feedback" class="link item" href="{% url 'users:feedback' %}">
|
||||
<i class="grey list icon"></i>
|
||||
<span class="v-nav-title">反馈与建议</span>
|
||||
</a>
|
||||
</div>
|
@ -0,0 +1,19 @@
|
||||
{% if is_paginated %}
|
||||
<div class="video-page">
|
||||
<div class="ui circular labels">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="ui circular label" href="?page={{ page_obj.previous_page_number }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}"><</a>
|
||||
{% endif %}
|
||||
{% for i in page_list %}
|
||||
{% if page_obj.number == i %}
|
||||
<a class="ui red circular label">{{ i }}</a>
|
||||
{% else %}
|
||||
<a class="ui circular label" href="?page={{ i }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}">{{ i }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a class="ui circular label" href="?page={{ page_obj.next_page_number }}{% if c %}&c={{c}}{% endif %}{% if q %}&q={{q}}{% endif %}">></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
@ -0,0 +1,28 @@
|
||||
{% load video_tag %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% for item in comments %}
|
||||
<div class="comment">
|
||||
<a class="avatar">
|
||||
|
||||
{% thumbnail item.avatar "200x200" crop="center" as im %}
|
||||
<img class="ui avatar image" src="{{ im.url }}">
|
||||
{% empty %}
|
||||
<img class="ui avatar image" src="{% static 'img/img_default_avatar.png' %}">
|
||||
{% endthumbnail %}
|
||||
|
||||
</a>
|
||||
<div class="content">
|
||||
<a class="author">
|
||||
{% if item.nickname %} {{item.nickname}} {% else %} 匿名 {% endif %}
|
||||
</a>
|
||||
<div class="metadata">
|
||||
<span class="date">{{ item.timestamp|time_since }}</span>
|
||||
</div>
|
||||
<div class="text">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
@ -0,0 +1,62 @@
|
||||
{% load static %}
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/semantic.custom.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/admin.css' %}"/>
|
||||
<title>后台管理</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ui inverted huge borderless fixed fluid menu">
|
||||
<a class="header item" href="{% url 'myadmin:index' %}">
|
||||
<img src="{% static 'img/logo.png' %}" id="logo">后台管理
|
||||
</a>
|
||||
<div class="right menu">
|
||||
<a class="item" href="{% url 'home' %}" target="_blank">前台首页</a>
|
||||
<a class="item" href="{% url 'myadmin:index' %}">后台首页</a>
|
||||
<a class="item">{{ user }}</a>
|
||||
<a class="item" href="{% url 'myadmin:logout' %}">退出</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<div class="column" id="sidebar">
|
||||
<div class="ui secondary vertical fluid menu">
|
||||
<a class="item" href="{% url 'myadmin:index' %}" id="index">总览</a>
|
||||
<div class="ui divider"></div>
|
||||
<a class="item" href="{% url 'myadmin:video_list' %}" id="video_list">视频列表</a>
|
||||
<a class="item" href="{% url 'myadmin:video_add' %}" id="video_add">添加视频</a>
|
||||
<div class="ui divider"></div>
|
||||
<a class="item" href="{% url 'myadmin:classification_list' %}" id="classification_list">分类列表</a>
|
||||
<a class="item" href="{% url 'myadmin:classification_add' %}" id="classification_add">添加分类</a>
|
||||
<div class="ui divider"></div>
|
||||
<a class="item" href="{% url 'myadmin:user_list' %}" id="user_list">用户列表</a>
|
||||
<a class="item" href="{% url 'myadmin:user_add' %}" id="user_add">添加用户</a>
|
||||
<div class="ui divider"></div>
|
||||
<a class="item" href="{% url 'myadmin:comment_list' %}" id="comment_list">评论列表</a>
|
||||
<div class="ui divider"></div>
|
||||
<a class="item" href="{% url 'myadmin:subscribe' %}" id="subscribe">订阅通知</a>
|
||||
<a class="item" href="{% url 'myadmin:feedback_list' %}" id="feedback_list">用户反馈</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column" id="content">
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
<script src="https://cdn.staticfile.org/jquery/1.9.0/jquery.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script src="{% static 'js/myadmin/admin_nav.js' %}"></script>
|
||||
{% block javascript %}
|
||||
{% endblock javascript %}
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,43 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>分类添加</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:classification_add' %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label>分类名称</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
{{form.status}}
|
||||
<label>是否启用</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="ui primary button" type="submit">添加</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -0,0 +1,23 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>分类添加</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<h3>添加成功!</h3>
|
||||
<h3><a href="{% url 'myadmin:classification_list' %}"><<返回列表</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -0,0 +1,48 @@
|
||||
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>分类编辑</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:classification_edit' form.instance.pk %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label>分类名称</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
{{form.status}}
|
||||
<label>是否启用</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="ui primary button" type="submit">保存</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3 class="ui header six wide column">分类列表</h3>
|
||||
<div class="v-title-extra ten wide column">
|
||||
<div class="ui action input v-admin-search">
|
||||
<input type="text" placeholder="Search..." value="{{q}}" id="v-search">
|
||||
<button class="ui small button" id="search">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<table class="ui unstackable single line striped selectable table">
|
||||
<thead>
|
||||
<tr><th>#id</th><th>名称</th></th><th>状态</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody class="video-list">
|
||||
|
||||
{% for item in classification_list %}
|
||||
<tr classification-id="{{item.id}}">
|
||||
<td> {{item.id}}</td>
|
||||
<td> {{item.title}}</td>
|
||||
<td> {% if item.status %}启用{% else %}未启用{% endif %}</td>
|
||||
<td>
|
||||
<a class="ui primary button classification-edit" href="{% url 'myadmin:classification_edit' item.id %}">编辑</a>
|
||||
<a class="ui button classification-delete">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<h3>暂无数据</h3>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="6">
|
||||
{% include 'myadmin/page_nav.html' %}
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% include "myadmin/classification_list_modal.html" %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var search_url = "{% url 'myadmin:classification_list' %}"
|
||||
var api_classification_delete = "{% url 'myadmin:classification_delete' %}"
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/classification_list.js' %}"></script>
|
||||
{% endblock javascript %}
|
@ -0,0 +1,63 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3 class="ui header six wide column">评论列表</h3>
|
||||
<div class="v-title-extra ten wide column">
|
||||
<div class="ui action input v-admin-search">
|
||||
<input type="text" placeholder="Search..." value="{{q}}" id="v-search">
|
||||
<button class="ui small button" id="search">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<table class="ui unstackable single line striped selectable table">
|
||||
<thead>
|
||||
<tr><th>用户</th><th>昵称</th><th>评论内容</th></th><th>时间</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody class="video-list">
|
||||
|
||||
{% for item in comment_list %}
|
||||
<tr comment-id="{{item.id}}">
|
||||
<td> {{item.user_id}}</td>
|
||||
<td> {{item.nickname}}</td>
|
||||
<td> {{item.content}}</td>
|
||||
<td> {{item.timestamp|date:'Y-m-d H:i'}}</td>
|
||||
<td>
|
||||
<a class="ui button comment-delete">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<h3>暂无数据</h3>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="6">
|
||||
{% include 'myadmin/page_nav.html' %}
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% include "myadmin/comment_list_modal.html" %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var search_url = "{% url 'myadmin:comment_list' %}"
|
||||
var api_comment_delete = "{% url 'myadmin:comment_delete' %}"
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/comment_list.js' %}"></script>
|
||||
{% endblock javascript %}
|
@ -0,0 +1,15 @@
|
||||
<!--- 删除模态框 -->
|
||||
<div class="ui tiny modal delete">
|
||||
<div class="header">
|
||||
删除评论
|
||||
</div>
|
||||
<div class="image content">
|
||||
<div class="description">
|
||||
确认删除该条评论?
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui primary approve button">确认</div>
|
||||
<div class="ui deny button">取消</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,62 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3 class="ui header six wide column">反馈列表</h3>
|
||||
<div class="v-title-extra ten wide column">
|
||||
<div class="ui action input v-admin-search">
|
||||
<input type="text" placeholder="Search..." value="{{q}}" id="v-search">
|
||||
<button class="ui small button" id="search">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<table class="ui unstackable single line striped selectable table">
|
||||
<thead>
|
||||
<tr><th>内容</th><th>联系方式</th><th>时间</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody class="video-list">
|
||||
|
||||
{% for item in feedback_list %}
|
||||
<tr feedback-id="{{item.id}}">
|
||||
<td> {{item.content}}</td>
|
||||
<td> {{item.contact}}</td>
|
||||
<td> {{item.timestamp|date:'Y-m-d H:i'}}</td>
|
||||
<td>
|
||||
<a class="ui button feedback-delete">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<h3>暂无数据</h3>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="6">
|
||||
{% include 'myadmin/page_nav.html' %}
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% include "myadmin/feedback_list_modal.html" %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var search_url = "{% url 'myadmin:feedback_list' %}"
|
||||
var api_feedback_delete = "{% url 'myadmin:feedback_delete' %}"
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/feedback_list.js' %}"></script>
|
||||
{% endblock javascript %}
|
@ -0,0 +1,15 @@
|
||||
<!--- 删除模态框 -->
|
||||
<div class="ui tiny modal delete">
|
||||
<div class="header">
|
||||
确认删除
|
||||
</div>
|
||||
<div class="image content">
|
||||
<div class="description">
|
||||
确认删除该条内容?
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui primary approve button">确认</div>
|
||||
<div class="ui deny button">取消</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,47 @@
|
||||
{% load static %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||
<title>登录</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/semantic.custom.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
|
||||
</head>
|
||||
<body id="v-account-body">
|
||||
<div class="ui middle aligned center aligned grid">
|
||||
<div class="column v-account">
|
||||
<h2 class="ui teal image header">
|
||||
<img src="{% static 'img/logo.png' %}" class="image">
|
||||
<div class="content">
|
||||
后台管理系统
|
||||
</div>
|
||||
</h2>
|
||||
<form class="ui large form" novalidate method="post" action="{% url 'myadmin:login' %}"
|
||||
enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="ui stacked segment">
|
||||
<div class="field">
|
||||
<div class="ui left icon input">
|
||||
<i class="user icon"></i>
|
||||
{{form.username}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui left icon input">
|
||||
<i class="lock icon"></i>
|
||||
{{form.password}}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="next" value="{{ next }}"/>
|
||||
<button class="ui fluid large teal submit button" type="submit">登录</button>
|
||||
</div>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,23 @@
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
{% load video_tag %}
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<h3>{{ video.title }}</h3>
|
||||
<p>
|
||||
<a class="image" href="{{site_url}}{% url 'video:detail' video.id %}">
|
||||
<img class="ui image"
|
||||
style="object-fit: cover; object-position: center;height: 200px;width: 300px;"
|
||||
src="{{site_url}}{{video.cover}}">
|
||||
</a>
|
||||
</p>
|
||||
<p>{{video.desc}}</p>
|
||||
|
||||
<a class="ui button" href="{{site_url}}{% url 'video:detail' video.id %}">点击进入</a>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,20 @@
|
||||
{% if is_paginated %}
|
||||
<div class="video-page">
|
||||
<div class="ui borderless pagination menu">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="item" href="?page={{ page_obj.previous_page_number }}{% if q %}&q={{q}}{% endif %}"><i
|
||||
class="left arrow icon"></i></a></a>
|
||||
{% endif %}
|
||||
{% for i in page_list %}
|
||||
{% if page_obj.number == i %}
|
||||
<a class="active item">{{ i }}</a>
|
||||
{% else %}
|
||||
<a class="item" href="?page={{ i }}{% if q %}&q={{q}}{% endif %}">{{ i }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a class="item" href="?page={{ page_obj.next_page_number }}{% if q %}&q={{q}}{% endif %}"><i class="icon right arrow"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
@ -0,0 +1,39 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>网站设置</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:setting' 1 %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
{{form.switch_mail}}
|
||||
<label>开启邮件服务</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="ui primary button" type="submit">保存</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -0,0 +1,58 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>订阅通知</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
<div class="ui small input focus">
|
||||
<!--<input type="text" id="video_id" required placeholder="请输入视频id">-->
|
||||
<form class="ui form" novalidate method="post" action=""
|
||||
enctype="multipart/form-data" role="form">
|
||||
<div class="field">
|
||||
<label>请选择要推送的视频</label>
|
||||
|
||||
<div class="ui selection dropdown">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">请选择</div>
|
||||
<div class="menu">
|
||||
{% for item in video_list %}
|
||||
<div class="item" data-value="{{item.id}}">{{item.title}}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h4>
|
||||
<div class="ui tiny primary button" id="send-mail" video-id="{{video_id}}">通知订阅用户</div>
|
||||
</h4>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="send-mail-progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var api_send_mail = "{% url 'myadmin:subscribe' %}";
|
||||
|
||||
$(function(){
|
||||
$('.ui .dropdown').dropdown();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/send_mail.js' %}"></script>
|
||||
{% endblock javascript %}
|
||||
|
@ -0,0 +1,47 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>用户添加</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:user_add' %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label>用户名</label>
|
||||
{{ form.username }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>密码</label>
|
||||
{{ form.password }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
{{form.is_staff}}
|
||||
<label>管理员账号</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="ui primary button" type="submit">添加</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -0,0 +1,23 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>用户添加</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<h3>添加成功!</h3>
|
||||
<h3><a href="{% url 'myadmin:user_list' %}"><<返回列表</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -0,0 +1,47 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>用户编辑</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:user_edit' form.instance.pk %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label>用户名</label>
|
||||
{{ form.username }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
{{form.is_staff}}
|
||||
<label>管理员账号</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="ui primary button" type="submit">保存</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3 class="ui header six wide column">用户列表</h3>
|
||||
<div class="v-title-extra ten wide column">
|
||||
<div class="ui action input v-admin-search">
|
||||
<input type="text" placeholder="Search..." value="{{q}}" id="v-search">
|
||||
<button class="ui small button" id="search">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<table class="ui unstackable single line striped selectable table">
|
||||
<thead>
|
||||
<tr><th>用户名</th><th>账号类型</th><th>加入时间</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody class="video-list">
|
||||
|
||||
{% for item in user_list %}
|
||||
<tr user-id="{{item.id}}">
|
||||
<td> {{item.username}}</td>
|
||||
<td> {% if item.is_staff %}管理员{% else %}普通{% endif %}</td>
|
||||
<td> {{item.date_joined|date:'Y-m-d H:i'}}</td>
|
||||
<td>
|
||||
<a class="ui primary button user-edit" href="{% url 'myadmin:user_edit' item.id %}">编辑</a>
|
||||
<a class="ui button user-delete">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<h3>暂无数据</h3>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="6">
|
||||
{% include 'myadmin/page_nav.html' %}
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% include "myadmin/user_list_modal.html" %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var api_user_delete = "{% url 'myadmin:user_delete' %}";
|
||||
var search_url = "{% url 'myadmin:user_list' %}"
|
||||
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/user_list.js' %}"></script>
|
||||
{% endblock javascript %}
|
@ -0,0 +1,15 @@
|
||||
<!--- 删除模态框 -->
|
||||
<div class="ui tiny modal delete">
|
||||
<div class="header">
|
||||
删除用户
|
||||
</div>
|
||||
<div class="image content">
|
||||
<div class="description">
|
||||
确认删除该用户?
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui primary approve button">确认</div>
|
||||
<div class="ui deny button">取消</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,60 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>视频添加</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
{% csrf_token %}
|
||||
<label id="progress_label" class="ui large blue label" for="chunked_upload">
|
||||
<i class="upload icon"></i> 上传视频
|
||||
<input id="chunked_upload" type="file" name="the_file" hidden>
|
||||
</label>
|
||||
<div id="progress_layout" class="n">
|
||||
<div class="ui progress" data-percent="74" id="upload_progress">
|
||||
<div class="bar">
|
||||
<div class="progress"></div>
|
||||
</div>
|
||||
<div class="label" id="upload_label">正在上传...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="next_layout" class="n">
|
||||
<button id="next" class="ui primary right labeled icon button">
|
||||
<i class="right arrow icon"></i>
|
||||
下一步
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
|
||||
<script>
|
||||
var api_chunked_uplad = "{% url 'myadmin:api_chunked_upload' %}";
|
||||
var api_chunked_upload_complete = "{% url 'myadmin:api_chunked_upload_complete' %}";
|
||||
</script>
|
||||
|
||||
<script src="{% static 'js/upload/jquery.js' %}"></script>
|
||||
<script src="{% static 'js/upload/jquery.ui.widget.js' %}"></script>
|
||||
<script src="{% static 'js/upload/jquery.iframe-transport.js' %}"></script>
|
||||
<script src="{% static 'js/upload/jquery.fileupload.js' %}"></script>
|
||||
<script src="{% static 'js/upload/spark-md5.js' %}"></script>
|
||||
<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script src="{% static 'js/myadmin/video_upload.js' %}"></script>
|
||||
|
||||
{% endblock javascript %}
|
@ -0,0 +1,101 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>视频编辑</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:video_edit' form.instance.pk %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<input name="video_id" id="video_id" type="hidden">
|
||||
<div class="field">
|
||||
<label>视频标题</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>视频描述</label>
|
||||
{{ form.desc }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>视频分类</label>
|
||||
<div class="ui selection dropdown">
|
||||
{{ form.classification }}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">请选择</div>
|
||||
<div class="menu">
|
||||
{% for item in clf_list %}
|
||||
<div class="item" data-value="{{item.id}}">{{item.title}}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>发布状态</label>
|
||||
<div class="ui selection dropdown">
|
||||
{{form.status}}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">请选择</div>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="0">发布</div>
|
||||
<div class="item" data-value="1">取消发布</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>视频封面</label>
|
||||
{% thumbnail video.cover "300x200" crop="center" as im %}
|
||||
<img class="ui small image" src="{{ im.url }}">
|
||||
{% empty %}
|
||||
{% endthumbnail %}
|
||||
|
||||
<div class="v-form-field">
|
||||
<label class="ui large green label" for="id_cover">
|
||||
<i class="upload icon"></i> 上传封面
|
||||
{{form.cover}}
|
||||
</label>
|
||||
<span id="file_is_choose" class="n">文件已选择</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui primary button" type="submit">保存</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
$('.ui .dropdown').dropdown();
|
||||
|
||||
$("#id_cover").change(function(){
|
||||
$("#file_is_choose").show()
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock javascript %}
|
@ -0,0 +1,68 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3 class="ui header six wide column">视频列表</h3>
|
||||
<div class="v-title-extra ten wide column">
|
||||
<div class="ui action input v-admin-search">
|
||||
<input type="text" placeholder="Search..." value="{{q}}" id="v-search">
|
||||
<button class="ui small button" id="search">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<table class="ui unstackable single line striped selectable table">
|
||||
<thead>
|
||||
<tr><th>#id</th><th>标题</th><th>分类</th><th>状态</th><th>访问次数</th><th>创建时间</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody class="video-list">
|
||||
|
||||
{% for item in video_list %}
|
||||
<tr video-id="{{item.id}}">
|
||||
<td> {{item.id}}</td>
|
||||
<td> {{item.title|default:""}}</td>
|
||||
<td> {{item.classification|default:""}}</td>
|
||||
<td> {{item.get_status_display}}</td>
|
||||
<td> {{item.view_count}}</td>
|
||||
<td> {{item.create_time|date:'Y-m-d H:i'}}</td>
|
||||
<td>
|
||||
<a class="ui primary button video-edit" href="{% url 'myadmin:video_edit' item.id %}">编辑</a>
|
||||
<a class="ui button video-delete">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<h3>暂无数据</h3>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="6">
|
||||
{% include 'myadmin/page_nav.html' %}
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% include "myadmin/video_list_modal.html" %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
<script>
|
||||
var api_video_delete = "{% url 'myadmin:video_delete' %}";
|
||||
var search_url = "{% url 'myadmin:video_list' %}"
|
||||
|
||||
</script>
|
||||
<script src="{% static 'js/myadmin/video_list.js' %}"></script>
|
||||
{% endblock javascript %}
|
@ -0,0 +1,15 @@
|
||||
<!--- 删除模态框 -->
|
||||
<div class="ui tiny modal delete">
|
||||
<div class="header">
|
||||
删除视频
|
||||
</div>
|
||||
<div class="image content">
|
||||
<div class="description">
|
||||
确认删除该条视频?
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui primary approve button" href="{{ request.path }}">确认</div>
|
||||
<div class="ui deny button">取消</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,93 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>视频资料</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<form class="ui form" novalidate method="post" action="{% url 'myadmin:video_publish' form.instance.pk %}"
|
||||
enctype="multipart/form-data" role="form">
|
||||
{% csrf_token %}
|
||||
<input name="video_id" id="video_id" type="hidden">
|
||||
<div class="field">
|
||||
<label>视频标题</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>视频描述</label>
|
||||
{{ form.desc }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>视频分类</label>
|
||||
<div class="ui selection dropdown">
|
||||
{{ form.classification }}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text">请选择</div>
|
||||
<div class="menu">
|
||||
{% for item in clf_list %}
|
||||
<div class="item" data-value="{{item.id}}">{{item.title}}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{form.status}}
|
||||
<div class="field">
|
||||
<label>视频封面</label>
|
||||
{% if video.cover %}
|
||||
{% thumbnail video.cover "300x200" crop="center" as im %}
|
||||
<img class="ui small image" src="{{ im.url }}">
|
||||
{% empty %}
|
||||
{% endthumbnail %}
|
||||
{% endif %}
|
||||
|
||||
<div class="v-form-field">
|
||||
<label class="ui large green label" for="id_cover">
|
||||
<i class="upload icon"></i> 上传封面
|
||||
{{form.cover}}
|
||||
</label>
|
||||
<span id="file_is_choose" class="n">文件已选择</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="ui primary button" type="submit">发布</button>
|
||||
|
||||
{% include "base/form_errors.html" %}
|
||||
{% include "base/form_messages.html" %}
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block modal %}
|
||||
{% endblock modal %}
|
||||
|
||||
{% block javascript %}
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
$('.ui .dropdown').dropdown();
|
||||
|
||||
$("#id_cover").change(function(){
|
||||
$("#file_is_choose").show()
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock javascript %}
|
@ -0,0 +1,23 @@
|
||||
{% extends 'myadmin/base.html' %}
|
||||
{% load static %}
|
||||
{% load thumbnail %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui grid">
|
||||
<div class="row">
|
||||
<h3>视频资料</h3>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="row">
|
||||
<div class="v-form-wrap">
|
||||
|
||||
<h3>发布成功!</h3>
|
||||
<h3><a href="{% url 'myadmin:video_list' %}"><<返回列表</a></h3>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|