后台管理站点 -- 3.文章管理

文章搜索功能

1.分析

请求方法GET
url定义/admin/news/
请求参数:字符串参数传参

参数 类型 前端是否必须传 描述
start_time 字符串 开始时间
end_time 字符串 结束时间
title 字符串 文章标题
author_name 字符串 作者姓名
tag_id 字符串 标签id
page 字符串 页数
2.后端视图
  • views.py
import json
import logging
from datetime import datetime
from urllib.parse import urlencode

from django.http import Http404, JsonResponse
from django.views import View
from django.db.models import Count
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin

from Dreamblog import settings
from admin import forms
from news import models
from . import constants
from .scripts import paginator_script
from utils.json_translate.json_fun import to_json_data
from utils.res_code.rescode import Code, error_map

# 后台文章搜索
class NewsManageView(PermissionRequiredMixin, View):
    """
    1.创建类视图
    2.从前端获取参数
        -- 传参方式:查询字符串方式 ?author=author&
    3.查询数据
    4.分页操作
    5.模板渲染
    """
    permission_required = ('news.add_news','news.view_news')
    raise_exception = True

    def get(self, request):
        """
        获取文章列表信息
        :param request:
        :return:
        """
        # 数据库提供数据
        tags = models.Tag.objects.only('id', 'name').filter(is_delete=False)
        newses = models.News.objects.select_related('author','tag').\
            only('id', 'title', 'author__username', 'tag__name', 'update_time').\
            filter(is_delete=False)

        # 通过时间进行过滤
        # 将时间字符串转换与数据库相同的时间参数(datat格式)
        # datetime.strftime() f->from time->str
        # datetime.strptime() p->pass str->time
        # format: 'yyyy/mm/dd', '%Y/%m/%d' 前后文呼应
        try:
            start_time = request.GET.get('start_time','')
            start_time = datetime.strptime(start_time,'%Y/%m/%d') if start_time else ''
        except Exception as e:
            logger.info("用户输入的时间有误:\n{}".format(e))
            start_time = ''

        try:
            end_time = request.GET.get('end_time','')
            end_time = datetime.strptime(end_time,'%Y/%m/%d') if end_time else ''
        except Exception as e:
            logger.info("用户输入的时间有误:\n{}".format(e))
            end_time = ''

        # lte <=     gte >=
        if start_time and not end_time:
            newses = newses.filter(update_time__lte=start_time)
        if end_time and not start_time:
            newses = newses.filter(update_time__gte=end_time)

        # 判断时间前后是否颠倒  __range  取范围,若开始时间小于结束时间为空
        if start_time and end_time:
            newses = newses.filter(update_time__range=(start_time,end_time))

            #优化:若newses查询集为空,就没必要执行下面代码

        # 通过title进行过滤
        title = request.GET.get('title','')
        if title:
            newses:newses.filter(title__icontains=title)

        # 通过作者名进行过滤
        author_name = request.GET.get('author_name','')
        if author_name:
            newses = newses.filter(author__username__icontains=author_name)

        # 通过标签id进行过滤
        try:
            tag_id = int(request.GET.get('tag_id',0))
        except Exception as e:
            logger.info("标签错误:\n{}".format(e))
            tag_id = 0

        newses = newses.filter(is_delete=False, tag_id=tag_id) or \
                    newses.filter(is_delete=False)

        # 分页处理
        try:
            page = int(request.GET.get('page', 1))
            if page == 0:
                page = 1
        except Exception as e:
            logger.info("当前页数错误:\n{}".format(e))
            page = 1

        # (可迭代对象,每页显示数目)
        paginator = Paginator(newses, constants.PER_PAGE_NEWS_COUNT)

        try:
            news_info = paginator.page(page)
        except EmptyPage:
            # 若用户访问的页数大于实际页数,则返回最后一页数据
            news_info = paginator.page(paginator.num_pages)

        # 分页算法
        paginator_data = paginator_script.get_paginator_data(paginator,news_info)

        start_time = start_time.strftime('%Y/%m/%d') if start_time else ''
        end_time = end_time.strftime('%Y/%m/%d') if end_time else ''

        context = {
            'news_info':news_info,
            'tags':tags,
            'paginator':paginator,
            'start_time':start_time,
            'end_time':end_time,
            'author_name':author_name,
            'tag_id':tag_id,
            "other_param":urlencode({
                'start_time':start_time,
                'end_time':end_time,
                'author_name':author_name,
                'tag_id':tag_id,
            })
        }
        context.update(paginator_data)

        return render(request, 'admin/news/news_manage.html',context=context)
# 创建apps/admin/constants.py文件
# 每页新闻数
PER_PAGE_NEWS_COUNT = 8
  • 路由 urls.py
from django.urls import path
from . import views

app_name='admin'

urlpatterns = [
    path('index/', views.AdminIndexView.as_view(), name='admin_index'),
    path('tags/', views.TagsManageView.as_view(), name='admin_tags'),
    path('tags//', views.TagEditView.as_view(), name='tag_edit'),
    path('news/', views.NewsManageView.as_view(), name='news_manage'),
]
  • 分页算法
# 创建scripts/paginator_script.py文件,定义如下方法:
def get_paginator_data(paginator, current_page, around_count=3):
    """
    :param paginator: 分页对象
    :param current_page: 当前页数据
    :param around_count: 显示的页码数
    :return: 当前页码、总页数、左边是否有更多页标记、右边是否有更多标记
    左边页码范围、右边页码范围
    """
    current_page_num = current_page.number  # 获取当前页面所在的页码
    total_page_num = paginator.num_pages  # 获取总页数

    left_has_more_page = False  # 默认左边没有更多页
    right_has_more_page = False  # 默认右边没有更多页

    # 算出当前页面左边的页码
    left_start_index = current_page_num - around_count
    left_end_index = current_page_num
    if current_page_num <= around_count * 2 + 1:
        left_page_range = range(1, left_end_index)
    else:
        left_has_more_page = True
        left_page_range = range(left_start_index, left_end_index)

    right_start_index = current_page_num + 1
    right_end_index = current_page_num + around_count + 1
    if current_page_num >= total_page_num - around_count * 2:
        right_page_range = range(right_start_index, total_page_num + 1)
    else:
        right_has_more_page = True
        right_page_range = range(right_start_index, right_end_index)

    return {
        "current_page_num": current_page_num,
        "total_page_num": total_page_num,
        "left_has_more_page": left_has_more_page,
        "right_has_more_page": right_has_more_page,
        "left_pages": left_page_range,
        "right_pages": right_page_range,
    }
  • 前端代码


{% extends 'admin/base/base.html' %}


{% block title %}
 文章管理页
{% endblock %}

{% block content_header %}
  文章管理
{% endblock %}

{% block header_option_desc %}
  正确的决策来自众人的智慧
{% endblock %}


{% block content %}
  
 
 
{% if start_time %} {% else %} {% endif %} - {% if end_time %} {% else %} {% endif %}
{% if title %} {% else %} {% endif %}
{% if author_name %} {% else %} {% endif %}
清除查询
{% for one_news in news_info %} {% endfor %}
标题 作者 标签 发布时间 操作
{{ one_news.title }} {{ one_news.author.username }} {{ one_news.tag.name }} {{ one_news.update_time }} 编辑 删除
{% endblock %} {% block script %} {% endblock %}
// 创建static/js/admin/news/news_manage.js文件

$(function () {
  let $startTime = $("input[name=start_time]");
  let $endTime = $("input[name=end_time]");
  const config = {
    // 自动关闭
    autoclose: true,
    // 日期格式
    format: 'yyyy/mm/dd',
    // 选择语言为中文
    language: 'zh-CN',
    // 优化样式
    showButtonPanel: true,
    // 高亮今天
    todayHighlight: true,
    // 是否在周行的左侧显示周数
    calendarWeeks: true,
    // 清除
    clearBtn: true,
    // 0 ~11  网站上线的时候
    startDate: new Date(2018, 10, 1),
    // 今天
    endDate: new Date(),
  };
  $startTime.datepicker(config);
  $endTime.datepicker(config);

  // 删除标签
  let $newsDel = $(".btn-del");  // 1. 获取删除按钮
  $newsDel.click(function () {   // 2. 点击触发事件
    let _this = this;
    let sNewsId = $(this).data('news-id');
    swal({
      title: "确定删除这篇文章吗?",
      text: "删除之后,将无法恢复!",
      type: "warning",
      showCancelButton: true,
      confirmButtonColor: "#DD6B55",
      confirmButtonText: "确定删除",
      cancelButtonText: "取消",
      closeOnConfirm: true,
      animation: 'slide-from-top',
    }, function () {

      $.ajax({
        // 请求地址
        url: "/admin/news/" + sNewsId + "/",  // url尾部需要添加/
        // 请求方式
        type: "DELETE",
        dataType: "json",
      })
        .done(function (res) {
          if (res.errno === "200") {
            // 更新标签成功
            message.showSuccess("标签删除成功");
            $(_this).parents('tr').remove();
          } else {
            swal({
              title: res.errmsg,
              type: "error",
              timer: 1000,
              showCancelButton: false,
              showConfirmButton: false,
            })
          }
        })
        .fail(function () {
          message.showError('服务器超时,请重试!');
        });
    });

  });


  // get cookie using jQuery
  function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      let cookies = document.cookie.split(';');
      for (let i = 0; i < cookies.length; i++) {
        let 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));
  }

  // Setting the token on the AJAX request
  $.ajaxSetup({
    beforeSend: function (xhr, settings) {
      if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
      }
    }
  });

});

特定文章删除,更新,查看功能

1.分析

请求方法GET、DELETE、PUT
url定义: 'news//'
请求参数:url路径传参

参数 类型 前端是否必须传 描述
news_id 字符串 文章id
# 后台文章管理
class NewsEditView(PermissionRequiredMixin,View):
    """
    1.权限校验
    2.get -- 渲染需更新文章界面
    3.put -- 更新文章
    4.delete -- 删除文章
    """
    permission_required = ('news.change_news','news.delete_news', 'news.view_news')
    raise_exception = True

    def handle_no_permission(self):
        if self.request.method.lower() != 'get':
            return to_json_data(errno=Code.ROLEERR, errmsg='没有操作权限')
        else:
            return super(NewsEditView, self).handle_no_permission()

    def get(self, request, news_id):
        """
        1.校验文章是否存在
        2.获取数据
        3.渲染前端界面
        :param request:
        :param news_id:
        :return:
        """
        news = models.News.objects.filter(is_delete=False,id=news_id).first()
        if news:
            tags = models.Tag.objects.only('id','name').filter(is_delete=False)
            context = {
                'tags':tags,
                'news':news,
            }
            return render(request, 'admin/news/news_pub.html',context=context)

        else:
            raise Http404('需要更新的文章不存在')

    def delete(self,request, news_id):
        """
        1.校验文章是否存在
        2.删除数据
        3.返回前端 True/False
        :param request:
        :param news_id:
        :return:
        """
        news = models.News.objects.only('id').filter(id=news_id).first()
        if news:
            news.is_delete = True
            news.save(update_fields = ['is_delete'])
            return to_json_data(errmsg="文章删除成功")
        else:
            return to_json_data(errno=Code.PARAMERR, errmsg="需要删除的文章不存在")

    def put(self, request, news_id):
        """
        更新文章
        :param request:
        :param news_id:
        :return:
        """
        news = models.News.objects.filter(is_delete=False, id=news_id).first()
        if not news:
            return to_json_data(errno=Code.NODATA, errmsg='需要更新的文章不存在')

        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
        # 将json转化为dict
        dict_data = json.loads(json_data.decode('utf8'))

        form = forms.NewsPubForm(data=dict_data)
        if form.is_valid():
            news.title = form.cleaned_data.get('title')
            news.digest = form.cleaned_data.get('digest')
            news.content = form.cleaned_data.get('content')
            news.image_url = form.cleaned_data.get('image_url')
            news.tag = form.cleaned_data.get('tag')
            news.save()
            return to_json_data(errmsg='文章更新成功')
        else:
            # 定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))
            err_msg_str = '/'.join(err_msg_list)  # 拼接错误信息为一个字符串

            return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)

文章发布功能

1.分析

请求方法GET、POST
url定义: 'news/pub/'
请求参数:body

参数 类型 前端是否必须传 描述
news_id 字符串 文章id
# 后台文章发布
class NewsPubView(PermissionRequiredMixin, View):
    """

    """
    permission_required = ('news.add_news','news.view_news')
    raise_exception = True

    def handle_no_permission(self):
        if self.request.method.lower() != 'get':
            return to_json_data(errno=Code.ROLEERR, errmsg='没有操作权限')
        else:
            return super(NewsPubView, self).handle_no_permission()

    def get(self,request):
        """
        1.获取文章标签
        2.渲染文章发布页
        :param request:
        :return:
        """
        tags = models.Tag.objects.only('id','name').filter(is_delete=False)

        return render(request, 'admin/news/news_pub.html', locals())
    def post(self,request):
        """
        需要将文章保存到数据库
        新增文章
        1.从前端获取参数
        2.校验参数
        3.把数据保存到数据库
        4.返回给前端执行结果 -- ok/false
        :param request:
        :return:
        """
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
        # 将json转化为dict
        dict_data = json.loads(json_data.decode('utf8'))

        form = forms.NewsPubForm(data=dict_data)
        if form.is_valid():
            # 只有继承model.Form,会提供一个save方法,利用表单对象.save直接保存,并写入数据库
            # commit=true (默认)直接保存并写入数据库 news_instance = form.save(commit=False),先不写入数据库,在添加其他数据信息后在调用news_instance.save(),保存并写入数据库
            news_instance = form.save(commit=False)
            # 如果没有设置作者信息不会保存,因为外键允许为空,但是创建没错,打开其他界面会出错,
            news_instance.author_id = request.user.id
            # news_instance.author= request.user 传入实例对象也可以
            news_instance.save()

            # 若不用上述方法
            # n = model.News(**form.cleaned_data)
            # n.title = form.cleaned_data('title')
            # n.save()
            return to_json_data(errmsg='文章创建成功')
        else:
            # 定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))
            err_msg_str = '/'.join(err_msg_list)  # 拼接错误信息为一个字符串

            return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)
# 创建apps/admin/forms.py文件:

from django import forms
from news.models import News, Tag


class NewsPubForm(forms.ModelForm):
    """
    """
   # 创建模型时,允许为空,所以需要重写,定义不能为空
    image_url = forms.URLField(label='文章图片url',
                               error_messages={"required": "文章图片url不能为空"})
    # 限制tag_id范围,tag是外键指定的多个值的多选框,所以定义的字段类型为ModelChoiceField
    tag = forms.ModelChoiceField(queryset=Tag.objects.only('id').filter(is_delete=False),
                                 error_messages={"required": "文章标签id不能为空", "invalid_choice": "文章标签id不存在", }
                                 )

    class Meta: # 元数据信息
        # 指定那个数据库模型来创建表单
        model = News  # 与数据库模型关联
        # 需要关联的字段
        # exclude 排除
        # 此处tag 指的是文章分类的实例对象,并不是tag_id,会出问题
        fields = ['title', 'digest', 'content', 'image_url', 'tag']
        # 自定义报错信息(由于定义模型没有写,所以在此处写)
        error_messages = {
            'title': {
                'max_length': "文章标题长度不能超过150",
                'min_length': "文章标题长度大于1",
                # 传入字符串为空和,传入为空格是有区别的
                'required': '文章标题不能为空',
            },
            'digest': {
                'max_length': "文章摘要长度不能超过200",
                'min_length': "文章标题长度大于1",
                'required': '文章摘要不能为空',
            },
            'content': {
                'required': '文章内容不能为空',
            },
        }
   
from django.db import models
from utils._Models._models import BaseModel
# 增加最小长度校验器
from django.core.validators import MinLengthValidator

class News(BaseModel):
    """
    super: create_time update_time is_delete
    built-in: title digest content clicks image_url
    ForeignKey: tag  author
    """
    # MinLengthValidator(1) 长度不小于1
    title = models.CharField(max_length=150, validators=[MinLengthValidator(1),], verbose_name="标题", help_text="标题")
    digest = models.CharField(max_length=200, validators=[MinLengthValidator(1),], verbose_name="摘要", help_text="摘要")
    content = models.TextField(verbose_name="新闻内容", help_text="新闻内容")
    clicks = models.IntegerField(default=0, verbose_name="点击量", help_text="点击量")
    image_url = models.URLField(default="", verbose_name="图片url", help_text="图片url", )

    tag = models.ForeignKey('Tag',on_delete=models.SET_NULL, null=True)
    author = models.ForeignKey('users.Users', on_delete=models.SET_NULL, null=True)

    class Meta:
        ordering = ['-update_time', '-id']
        db_table = 'tb_news'
        verbose_name = '新闻'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title
  • fastfds 功能实现
# 图片上传至FastDFS服务器功能实现

class NewsUploadImage(PermissionRequiredMixin, View):
    """
    """
    permission_required = ('news.add_news',)

    def handle_no_permission(self):
        return to_json_data(errno=Code.ROLEERR, errmsg='没有上传图片的权限')

    def post(self, request):
        # request.FILES.get('image_file') 获取图片对象
        image_file = request.FILES.get('image_file')
        if not image_file:
            logger.info('从前端获取图片失败')
            return to_json_data(errno=Code.NODATA, errmsg='从前端获取图片失败')
        # 文件类型有content_type这个属性
        if image_file.content_type not in ('image/jpeg', 'image/png', 'image/gif'):
            return to_json_data(errno=Code.DATAERR, errmsg='不能上传非图片文件')

        # image_file.name 文件名
        try:
            image_ext_name = image_file.name.split('.')[-1]
        except Exception as e:
            logger.info('图片拓展名异常:{}'.format(e))
            image_ext_name = 'jpg'

        try:
            # 前端传的是文件 需要通过upload_by_buffer() 方法,
            FDFS_Client = Fdfs_client('utils/fastdfs/client.conf')
            upload_res = FDFS_Client.upload_by_buffer(image_file.read(), file_ext_name=image_ext_name)
        except Exception as e:
            logger.error('图片上传出现异常:{}'.format(e))
            return to_json_data(errno=Code.UNKOWNERR, errmsg='图片上传异常')
        else:
            # 此处有个点Upload successed.
            if upload_res.get('Status') != 'Upload successed.':
                logger.info('图片上传到FastDFS服务器失败')
                return to_json_data(Code.UNKOWNERR, errmsg='图片上传到服务器失败')
            else:
                image_name = upload_res.get('Remote file_id')
                # from django.conf import settings
                # 定义域名 FASTDFS_SERVER_DOMAIN = "http://127.0.0.1:8888/",配置文件里的
                image_url = settings.FASTDFS_SERVER_DOMAIN + image_name
                return to_json_data(data={'image_url': image_url}, errmsg='图片上传成功')
# utils。fastdfs.fdfs.py
from fdfs_client.client import Fdfs_client

# 指定fdfs客户端配置文件所在路径
FDFS_Client = Fdfs_client('utils/fastdfs/client.conf')

if __name__ == '__main__':
    try:
        # 此处指定图片路径上传的,知道文件后缀 upload_by_filename()
        ret = FDFS_Client.upload_by_filename('media/captcha.png')
    except Exception as e:
        print("fdfs测试异常:{}".format(e))
    else:
        print(ret)
  • 七牛云 功能实现
# 在虚拟机中安装七牛云所需模块
pip install qiniu
# 创建utils/secrets/qiniu_secret_info.py文件

# 从七牛云"个人中心>密钥管理"中获取自己的 Access Key 和 Secret Key

QI_NIU_ACCESS_KEY = '你自己七牛云上的AK'
QI_NIU_SECRET_KEY = '你自己七牛云上的SK'
QI_NIU_BUCKET_NAME = '你自己在七牛云上创建的存储空间名'
# 并将qiniu_secret_info.py添加到.gitignore中,让该文件不上传
qiniu_secret_info.py
import qiniu

from utils.SECRET import qiniu_secret_info


class UploadToken(PermissionRequiredMixin, View):
    """
    """
    permission_required = ('news.add_news', 'news.view_news')

    def handle_no_permission(self):
        return to_json_data(errno=Code.ROLEERR, errmsg='没有相关权限')

    def get(self, request):
        access_key = qiniu_secret_info.QI_NIU_ACCESS_KEY
        secret_key = qiniu_secret_info.QI_NIU_SECRET_KEY
        bucket_name = qiniu_secret_info.QI_NIU_BUCKET_NAME
        # 构建鉴权对象
        q = qiniu.Auth(access_key, secret_key)
        token = q.upload_token(bucket_name)
        # 最好直接返回原生js
        return JsonResponse({"uptoken": token})
# 在apps/admin/urls.py中添加如下路由:

urlpatterns = [
    path('news//', views.NewsEditView.as_view(), name='news_edit'),
    path('news/pub/', views.NewsPubView.as_view(), name='news_pub'),
    path('news/images/', views.NewsUploadImage.as_view(), name='upload_image'),
    path('token/', views.UploadToken.as_view(), name='upload_token'),  # 七牛云上传图片需要调用token
    
  • 前端代码



{% extends 'admin/base/base.html' %}


{% block title %}
  文章发布页
{% endblock %}

{% block content_header %}
  文章发布
{% endblock %}

{% block header_option_desc %}
  书是人类进步的阶梯
{% endblock %}

{% block content %}
{% if news %} {% else %} {% endif %}
{% if news %} {% else %} {% endif %}
{% if news %} {% else %} {% endif %}
{% if news %}
{% else %}
{% endif %}
{% endblock %} {% block script %} {# 导入七牛云需要的3个js文件 #} {# 这3个js文件有依赖关系,qiniu.min.js需要放在后面 #} {% endblock %}
// 创建static/js/admin/news/news_pub.js文件

$(function () {
  let $e = window.wangEditor;
  window.editor = new $e('#news-content');
  window.editor.create();

  // 获取缩略图输入框元素
  let $thumbnailUrl = $("#news-thumbnail-url");

  // ================== 上传图片文件至服务器 ================
  let $upload_to_server = $("#upload-news-thumbnail");
  $upload_to_server.change(function () {
    let file = this.files[0];   // 获取文件
    let oFormData = new FormData();  // 创建一个 FormData
    oFormData.append("image_file", file); // 把文件添加进去
    // 发送请求
    $.ajax({
      url: "/admin/news/images/",
      method: "POST",
      data: oFormData,
      processData: false,   // 定义文件的传输
      contentType: false,
    })
      .done(function (res) {
        if (res.errno === "0") {
          // 更新标签成功
          message.showSuccess("图片上传成功");
          let sImageUrl = res["data"]["image_url"];
          // console.log(thumbnailUrl);
          $thumbnailUrl.val('');
          $thumbnailUrl.val(sImageUrl);
        } else {
          message.showError(res.errmsg)
        }
      })
      .fail(function () {
        message.showError('服务器超时,请重试!');
      });

  });


  // ================== 上传至七牛(云存储平台) ================
  let $progressBar = $(".progress-bar");
  QINIU.upload({
    "domain": "http://pl3yncr1e.bkt.clouddn.com/",  // 七牛空间域名
    "uptoken_url": "/admin/token/",  // 后台返回 token的地址
    "browse_btn": "upload-btn",     // 按钮
    "success": function (up, file, info) {   // 成功
      let domain = up.getOption('domain');
      let res = JSON.parse(info);
      let filePath = domain + res.key;
      console.log(filePath);
      $thumbnailUrl.val('');
      $thumbnailUrl.val(filePath);
    },
    "error": function (up, err, errTip) {
      // console.log('error');
      console.log(up);
      console.log(err);
      console.log(errTip);
      // console.log('error');
      message.showError(errTip);
    },
    "progress": function (up, file) {
      let percent = file.percent;
      $progressBar.parent().css("display", 'block');
      $progressBar.css("width", percent + '%');
      $progressBar.text(parseInt(percent) + '%');
    },
    "complete": function () {
      $progressBar.parent().css("display", 'none');
      $progressBar.css("width", '0%');
      $progressBar.text('0%');
    }
  });


  // ================== 发布文章 ================
  let $newsBtn = $("#btn-pub-news");
  $newsBtn.click(function () {
    // 判断文章标题是否为空
    let sTitle = $("#news-title").val();  // 获取文章标题
    if (!sTitle) {
        message.showError('请填写文章标题!');
        return
    }
    // 判断文章摘要是否为空
    let sDesc = $("#news-desc").val();  // 获取文章摘要
    if (!sDesc) {
        message.showError('请填写文章摘要!');
        return
    }

    let sTagId = $("#news-category").val();
    if (!sTagId || sTagId === '0') {
      message.showError('请选择文章标签');
      return
    }

    let sThumbnailUrl = $thumbnailUrl.val();
    if (!sThumbnailUrl) {
      message.showError('请上传文章缩略图');
      return
    }

    let sContentHtml = window.editor.txt.html();
    if (!sContentHtml || sContentHtml === '


') { message.showError('请填写文章内容!'); return } // 获取news_id 存在表示更新 不存在表示发表 let newsId = $(this).data("news-id"); let url = newsId ? '/admin/news/' + newsId + '/' : '/admin/news/pub/'; let data = { "title": sTitle, "digest": sDesc, "tag": sTagId, "image_url": sThumbnailUrl, "content": sContentHtml, }; $.ajax({ // 请求地址 url: url, // 请求方式 type: newsId ? 'PUT' : 'POST', data: JSON.stringify(data), // 请求内容的数据类型(前端发给后端的格式) contentType: "application/json; charset=utf-8", // 响应数据的格式(后端返回给前端的格式) dataType: "json", }) .done(function (res) { if (res.errno === "0") { if (newsId) { fAlert.alertNewsSuccessCallback("文章更新成功", '跳到后台首页', function () { window.location.href = '/admin/' }); } else { fAlert.alertNewsSuccessCallback("文章发表成功", '跳到后台首页', function () { window.location.href = '/admin/' }); } } else { fAlert.alertErrorToast(res.errmsg); } }) .fail(function () { message.showError('服务器超时,请重试!'); }); }); // get cookie using jQuery function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { let cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { let 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)); } // Setting the token on the AJAX request $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); } } }); });

最终展示

  • 查询文章


    后台管理站点 -- 3.文章管理_第1张图片
    image.png

    后台管理站点 -- 3.文章管理_第2张图片
    image.png
  • 发布文章


    后台管理站点 -- 3.文章管理_第3张图片
    image.png

    后台管理站点 -- 3.文章管理_第4张图片
    image.png
  • 更新文章


    后台管理站点 -- 3.文章管理_第5张图片
    image.png
  • 删除文章


    后台管理站点 -- 3.文章管理_第6张图片
    image.png

    后台管理站点 -- 3.文章管理_第7张图片
    image.png

你可能感兴趣的:(后台管理站点 -- 3.文章管理)