diff --git a/account/migrations/0001_initial.py b/account/migrations/0001_initial.py new file mode 100644 index 0000000..03d5584 --- /dev/null +++ b/account/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import account.models + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)), + ('username', models.CharField(unique=True, max_length=30)), + ('real_name', models.CharField(max_length=30, null=True, blank=True)), + ('email', models.EmailField(max_length=254, null=True, blank=True)), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('admin_type', models.IntegerField(default=0)), + ], + options={ + 'db_table': 'user', + }, + managers=[ + ('objects', account.models.UserManager()), + ], + ), + migrations.CreateModel( + name='AdminGroup', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ], + ), + ] diff --git a/account/migrations/__init__.py b/account/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/account/serializers.py b/account/serializers.py index d82b36d..358ced7 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -41,6 +41,6 @@ class EditUserSerializer(serializers.Serializer): id = serializers.IntegerField() username = serializers.CharField(max_length=30) real_name = serializers.CharField(max_length=30) - password = serializers.CharField(max_length=30, min_length=6, required=True) + password = serializers.CharField(max_length=30, min_length=6, required=False, default=None) email = serializers.EmailField(max_length=254) admin_type = serializers.IntegerField(default=0) diff --git a/account/tests.py b/account/tests.py index 4115829..ca69832 100644 --- a/account/tests.py +++ b/account/tests.py @@ -4,6 +4,7 @@ import json from django.core.urlresolvers import reverse from django.test import TestCase, Client from django.http import HttpResponse +from django.contrib import auth from rest_framework.test import APITestCase, APIClient from rest_framework.views import APIView @@ -194,11 +195,18 @@ class UserAdminAPITest(APITestCase): self.assertEqual(response.data, {"code": 1, "data": u"该用户不存在!"}) def test_success_user_edit_not_password(self): - data = {"id": 1, "username": "test0", "real_name": "test00", "password": "aaaaaa", + data = {"id": 1, "username": "test0", "real_name": "test00", "email": "60@qq.com", "admin_type": "2"} response = self.client.put(self.url, data=data) self.assertEqual(response.data["code"], 0) + def test_success_user_edit_change_password(self): + data = {"id": 1, "username": "test0", "real_name": "test00", "password": "111111", + "email": "60@qq.com", "admin_type": "2"} + response = self.client.put(self.url, data=data) + self.assertEqual(response.data["code"], 0) + self.assertIsNotNone(auth.authenticate(username="test0", password="111111")) + @login_required def login_required_FBV_test_without_args(request): diff --git a/account/views.py b/account/views.py index e128782..fe14464 100644 --- a/account/views.py +++ b/account/views.py @@ -129,7 +129,7 @@ class UserAPIView(APIView): admin_type = request.GET.get("admin_type", None) if admin_type: try: - user = user.filter(admin_type=int(admin_type)) + user = user.filter(admin_type__gte=int(admin_type)) except ValueError: return error_response(u"参数错误") keyword = request.GET.get("keyword", None) diff --git a/static/src/css/global.css b/static/src/css/global.css index c55a0bb..1782600 100644 --- a/static/src/css/global.css +++ b/static/src/css/global.css @@ -24,4 +24,8 @@ img { label { font-size: 16px; +} + +[ms-controller] { + display: none } \ No newline at end of file diff --git a/static/src/js/app/admin/announcement/announcement.js b/static/src/js/app/admin/announcement/announcement.js index 1d3cc28..6472ec1 100644 --- a/static/src/js/app/admin/announcement/announcement.js +++ b/static/src/js/app/admin/announcement/announcement.js @@ -1,34 +1,40 @@ require(["jquery", "avalon", "csrf", "bs_alert", "editor", "validation"], function ($, avalon, csrfHeader, bs_alert, editor) { announcementEditor = editor("#editor"); //创建新建公告的内容编辑器 - editAnnouncementEditor = null; + editAnnouncementEditor = editor("#editAnnouncementEditor"); - if (!avalon.vmodels.announcement) // 防止模式重新定义 - { - // avalon:定义模式 announcement - vm = avalon.define({ + avalon.vmodels.announcement = null; + + // avalon:定义模式 announcement + avalon.ready(function () { + var vm = avalon.define({ $id: "announcement", - announcement: [], // 公告列表数据项 - previous_page: 0, // 之前的页数 - next_page: 0, // 之后的页数 - page: 1, // 当前页数 - isEditing: 0, // 正在编辑的公告的ID, 为零说明未在编辑 + //通用变量 + announcement: [], // 公告列表数据项 + previous_page: 0, // 之前的页数 + next_page: 0, // 之后的页数 + page: 1, // 当前页数 + isEditing: 0, // 正在编辑的公告的ID, 为零说明未在编辑 + page_count: 1, // 总页数 + visableOnly: false, //仅显示可见公告 + // 编辑 + announcementVisible: 0, getState: function (el) { //获取公告当前状态,显示 if (el.visible) return "可见"; else return "隐藏"; }, - getNext: function (el) { + getNext: function () { if (!vm.next_page) return; - getPageData(++(vm.page)); + getPageData(vm.page + 1); }, - getPrevious: function (el) { + getPrevious: function () { if (!vm.previous_page) return; - getPageData(--(vm.page)); + getPageData(vm.page - 1); }, - getBtnClass: function (btn) { + getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 if (btn) { return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; } @@ -37,13 +43,10 @@ require(["jquery", "avalon", "csrf", "bs_alert", "editor", "validation"], functi } }, - enEdit: function (el) { //点击编辑按钮的事件,显示/隐藏编辑区 + enEdit: function (el) { //点击编辑按钮的事件,显示/隐藏编辑区 $("#newTitle").val(el.title); - if (!editAnnouncementEditor) //初始化编辑器 - editAnnouncementEditor = editor("#editAnnouncementEditor"); editAnnouncementEditor.setValue(el.content); - if (el.visible == false) - $("#hidden").attr("checked", true); + vm.announcementVisible = el.visible; if (vm.isEditing == el.id) vm.isEditing = 0; else @@ -54,21 +57,25 @@ require(["jquery", "avalon", "csrf", "bs_alert", "editor", "validation"], functi vm.isEditing = 0; }, submitChange: function () { // 处理编辑公告提交事件,顺便验证字段为空 - var title = $("#newTitle").val(), content = editAnnouncementEditor.getValue(), visible = true; - if ($("#hidden").attr("checked") == true) - visible = false; + var title = $("#newTitle").val(), content = editAnnouncementEditor.getValue(); if (title != "") { if (content != "") { $.ajax({ //发送修改公告请求 beforeSend: csrfHeader, - url: "/api/edit_announcements/", + url: "/api/admin/announcement/", dataType: "json", - method: "post", - data: {id: vm.isEditing, title: title, content: content, visible: visible}, + method: "put", + data: { + id: vm.isEditing, + title: title, + content: content, + visible: vm.announcementVisible + }, success: function (data) { if (!data.code) { bs_alert("修改成功"); vm.isEditing = 0; + getPageData(1); } else { bs_alert(data.data); @@ -83,72 +90,80 @@ require(["jquery", "avalon", "csrf", "bs_alert", "editor", "validation"], functi bs_alert("公告标题不能为空"); } }); - } - - avalon.scan(); - getPageData(1); //公告列表初始化 - vm.page = 1; - vm.isEditing = 0; - //Ajax get数据 - function getPageData(page) { - $.ajax({ - beforeSend: csrfHeader, - url: "/api/announcements/?paging=true&page=" + page + "&page_size=10", - dataType: "json", - method: "get", - success: function (data) { - if (!data.code) { - vm.announcement = data.data.results; - vm.previous_page = data.data.previous_page; - vm.next_page = data.data.next_page; - } - else { - bs_alert(data.data); - } - } + vm.$watch("visableOnly", function () { + getPageData(1); }); - } - //新建公告表单验证与数据提交 - $("#announcement-form") - .formValidation({ - framework: "bootstrap", - fields: { - title: { - validators: { - notEmpty: { - message: "请填写公告标题" + avalon.scan(); + + getPageData(1); //公告列表初始化 + + //Ajax get数据 + function getPageData(page) { + var visible = ''; + if (vm.visableOnly == true) + visible = "&visible=true"; + $.ajax({ + beforeSend: csrfHeader, + url: "/api/announcements/?paging=true&page=" + page + "&page_size=10" + visible, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.announcement = data.data.results; + vm.page_count = data.data.total_page; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + vm.page = page; + } + else { + bs_alert(data.data); + } + } + }); + } + + //新建公告表单验证与数据提交 + $("#announcement-form") + .formValidation({ + framework: "bootstrap", + fields: { + title: { + validators: { + notEmpty: { + message: "请填写公告标题" + } } } } } - } - ).on('success.form.fv', function (e) { - e.preventDefault(); - var title = $("#title").val(); - var content = announcementEditor.getValue(); - if (content == "") { - bs_alert("请填写公告内容"); - return; - } - $.ajax({ - beforeSend: csrfHeader, - url: "/api/admin/announcement/", - data: {title: title, content: content}, - dataType: "json", - method: "post", - success: function (data) { - if (!data.code) { - bs_alert("提交成功!"); - $("#title").val(""); - announcementEditor.setValue(""); - getPageData(1, function (data) { - }); - } else { - bs_alert(data.data); - } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var title = $("#title").val(); + var content = announcementEditor.getValue(); + if (content == "") { + bs_alert("请填写公告内容"); + return; } - }) - }); + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/announcement/", + data: {title: title, content: content}, + dataType: "json", + method: "post", + success: function (data) { + if (!data.code) { + bs_alert("提交成功!"); + $("#title").val(""); + announcementEditor.setValue(""); + getPageData(1); + } else { + bs_alert(data.data); + } + } + }) + }); + }); + }); \ No newline at end of file diff --git a/static/src/js/app/admin/user/user_list.js b/static/src/js/app/admin/user/user_list.js new file mode 100644 index 0000000..8522a21 --- /dev/null +++ b/static/src/js/app/admin/user/user_list.js @@ -0,0 +1,165 @@ +require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, avalon, csrfHeader, bs_alert) { + avalon.vmodels.user_list = null; + + // avalon:定义模式 user_list + avalon.ready(function () { + var vm = avalon.define({ + $id: "user_list", + //通用变量 + user_list: [], // 用户列表数据项 + previous_page: 0, // 之前的页数 + next_page: 0, // 之后的页数 + page: 1, // 当前页数 + isEditing: 0, // 正在编辑的公告的ID, 为零说明未在编辑 + page_count: 1, // 总页数 + user_type: ["一般用户", "管理员", "超级管理员"], + key_word: "", + showAdminOnly: false, + //编辑区域同步变量 + username: "", + real_name: "", + email: "", + admin_type: 0, + id: 0, + last_login: "", + create_time: "", + getNext: function () { + if (!vm.next_page) + return; + getPageData(vm.page + 1); + }, + getPrevious: function () { + if (!vm.previous_page) + return; + getPageData(vm.page - 1); + }, + getBtnClass: function (btn) { //上一页/下一页按钮启用禁用逻辑 + if (btn) { + return vm.next_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + else { + return vm.previous_page ? "btn btn-primary" : "btn btn-primary disabled"; + } + }, + enEdit: function (el) { //点击编辑按钮的事件,显示/隐藏编辑区 + vm.username = el.username; + vm.real_name = el.real_name; + vm.admin_type = el.admin_type; + vm.email = el.email; + vm.id = el.id; + if (vm.isEditing == el.id) + vm.isEditing = 0; + else + vm.isEditing = el.id; + }, + getPage: function (page_index) { + getPageData(page_index); + } + }); + vm.$watch("showAdminOnly", function () { + getPageData(1); + }); + avalon.scan(); + getPageData(1); //用户列表初始化 + //Ajax get数据 + function getPageData(page) { + var url = "/api/users/?paging=true&page=" + page + "&page_size=10"; + if (vm.showAdminOnly == true) + url += "&admin_type=1"; + if (vm.key_word != "") + url += "&keyword=" + vm.key_word; + $.ajax({ + beforeSend: csrfHeader, + url: url, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.user_list = data.data.results; + vm.page_count = data.data.total_page; + vm.previous_page = data.data.previous_page; + vm.next_page = data.data.next_page; + vm.page = page; + } + else { + bs_alert(data.data); + } + } + }); + } + + $("#edit_user-form") + .formValidation({ + framework: "bootstrap", + fields: { + username: { + validators: { + notEmpty: { + message: "请填写用户名" + }, + stringLength: { + min: 3, + max: 30, + message: '用户名长度必须在3到30位之间' + } + } + }, + real_name: { + validators: { + notEmpty: { + message: "请填写真实姓名" + } + } + }, + email: { + validators: { + notEmpty: { + message: "请填写电子邮箱邮箱地址" + }, + emailAddress: { + message: "请填写有效的邮箱地址" + } + } + }, + password: { + validators: { + stringLength: { + min: 6, + max: 30, + message: '密码长度必须在6到30位之间' + } + } + } + } + } + ).on('success.form.fv', function (e) { + e.preventDefault(); + var data = { + username: vm.username, + real_name: vm.real_name, + email: vm.email, + id: vm.id, + admin_type: vm.admin_type + }; + if ($("#password").val() !== "") + data.password = $("#password").val(); + $.ajax({ + beforeSend: csrfHeader, + url: "/api/admin/user/", + data: data, + dataType: "json", + method: "put", + success: function (data) { + if (!data.code) { + bs_alert("提交成功!"); + getPageData(1); + $("#password").val(""); + } else { + bs_alert(data.data); + } + } + }) + }); + }); + +}) \ No newline at end of file diff --git a/template/admin/admin.html b/template/admin/admin.html index e3bc97b..9474283 100644 --- a/template/admin/admin.html +++ b/template/admin/admin.html @@ -97,13 +97,20 @@
-
+
diff --git a/template/admin/announcement/announcement.html b/template/admin/announcement/announcement.html
new file mode 100644
index 0000000..e113ff7
--- /dev/null
+++ b/template/admin/announcement/announcement.html
@@ -0,0 +1,65 @@
+| 编号 | +标题 | +创建时间 | +更新时间 | +创建者 | +状态 | +操作 | +
|---|---|---|---|---|---|---|
| {{el.id}} | +{{el.title}} | +{{el.create_time|date("yyyy-MM-dd HH:mm:ss")}} | +{{el.last_update_time|date("yyyy-MM-dd HH:mm:ss")}} | +{{el.created_by.username}} | +{{getState(el)}} | ++ + | +