renran-文章模块02

显示当前文集的文章列表

视图,代码:

from rest_framework.generics import ListAPIView
from .serializers import ArticleModelSerializer
from .models import Article
from rest_framework.response import Response
from rest_framework import status
class ArticleOfCollectionAPIView(ListAPIView):
    """文集下的文章"""
    serializer_class = ArticleModelSerializer
    permission_classes = [IsAuthenticated]  # 必须是登陆用户才能访问过来

    def list(self, request,  *args, **kwargs):
        user = request.user
        collection_id = request.query_params.get("collection_id")
        try:
            ArticleCollection.objects.get(pk=collection_id)
        except ArticleCollection.DoesNotExist:
            return Response({"message":"错误的文集ID"}, status=status.HTTP_400_BAD_REQUEST)

        queryset = Article.objects.filter(user=user,collection_id=collection_id).order_by("orders", "-id")
        queryset = self.filter_queryset(queryset)
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

序列化器,代码:

from .models import Article
class ArticleModelSerializer(serializers.ModelSerializer):
    """文章模型序列化器"""
    class Meta:
        model = Article
        fields = ["id","name","content","html_content","collection","pub_date","is_public"]

路由代码:

from django.urls import path,re_path
from . import views
urlpatterns = [
    path("collection/", views.CollecionAPIView.as_view()),
    re_path("^collection/(?P<pk>\d+)/$", views.CollecionAPIView.as_view()),
    re_path("^collection/article/$", views.ArticleOfCollectionAPIView.as_view()),
]

现有的接口实现了以后,会发现没有文章,所以我们需要把文章相关模型注册到xadmin中,让我们可以在xadmin中添加测试文章数据。

article/adminx.py,代码:

import xadmin

from .models import ArticleCollection
class ArticleCollectionModelAdmin(object):
    """文集"""
    list_display = ["id","name"]
    list_editable = ["name"]
xadmin.site.register(ArticleCollection,ArticleCollectionModelAdmin)


from .models import Article
class ArticleModelAdmin(object):
    """文章"""
    list_display=["id","name"]
xadmin.site.register(Article, ArticleModelAdmin)

完成上面注册以后,修改文章子应用在xadmin中的名称显示,

article/apps.py,代码:

from django.apps import AppConfig


class ArticleConfig(AppConfig):
    name = 'article'
    verbose_name="文章管理"

article/__init__.py,代码:

default_app_config = "article.apps.ArticleConfig"

访问xadmin的时候报错,错误提示:__str__方法相关错误。解决方案:

  1. users/models.py下模型的__str__方法代码调整:
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    mobile = models.CharField(max_length=15, null=True, unique=True, help_text="手机号码",verbose_name="手机号码")
    wxchat = models.CharField(max_length=100, null=True, unique=True, help_text="微信账号", verbose_name="微信账号")
    qq_number = models.CharField(max_length=11, null=True, unique=True, help_text="QQ号", verbose_name="QQ号")
    alipay = models.CharField(max_length=100, null=True, unique=True, help_text="支付宝账号", verbose_name="支付宝账号")
    # 保存文件的子目录 ImageField和FileField字段类型内置了文件上传处理类
    avatar = models.ImageField(upload_to="avatar", null=True, default=None, verbose_name="头像")
    money  = models.DecimalField(max_digits=8, decimal_places=2,default=0, help_text="账户余额", verbose_name="账户余额")
    nickname = models.CharField(max_length=20, null=True, unique=True, help_text="用户昵称",verbose_name="用户昵称")

    class Meta:
        db_table = "rr_users"
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.nickname if self.nickname else self.username

在utils/models.py,代码:

from django.db import models
class BaseModel(models.Model):
    """公共模型"""
    name = models.CharField(null=True, default="暂无", blank=True, max_length=150, verbose_name='名称')
    orders = models.IntegerField(default=0, verbose_name='显示顺序')
    is_show = models.BooleanField(default=False, verbose_name="是否上架")
    is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")
    created_time = models.DateTimeField(null=True,blank=True, auto_now_add=True, verbose_name="添加时间")
    updated_time = models.DateTimeField(null=True,blank=True, auto_now=True, verbose_name="更新时间")

    class Meta:
        # 设置当前模型在数据迁移的时候不要为它创建表
        abstract = True

    def __str__(self):
        return self.name if self.name else ""

前端展示当前文集的文章列表,代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn">
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" title="随笔"><span class="">随笔</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
            }
        },
        filters:{
           truncate(content){
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件
          },
          imgDel(pos) {
              // 删除文件
          }
        }
    }
</script>

切换文集以后,重新获取新的文集对应的文章列表。

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn">
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" title="随笔"><span class="">随笔</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
            }
        },
        filters:{
           truncate(content){
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件
          },
          imgDel(pos) {
              // 删除文件
          }
        }
    }
</script>

显示当前文章的操作菜单【扩展】

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
            }
        },
        filters:{
           truncate(content){
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件
          },
          imgDel(pos) {
              // 删除文件
          }
        }
    }
</script>


<style scoped>
  /* 在原有css代码基础上调整以下样式 */
  .rQQG7{
    height: 100%;
    display: block;
    width: 40%;
    border-right: 1px solid #d9d9d9;
  }
  ._3x4X_{
    top: 0;
    right: 100%;
    position: absolute;
  }
  ._24i7u {
    flex-shrink: 0;
    padding: 0 80px 10px 40px;
    margin-bottom: 0;
    border: none;
    font-size: 30px;
    font-weight: 400;
    line-height: 30px;
    box-shadow: none;
    color: #595959;
    background-color: transparent;
    outline: none;
    border-radius: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    position: absolute;
    top: 0;
    right: 0;
    width: 60%;
}
  #editor {
    margin: auto;
    width: 60%;
    position: absolute;
    right: 0;
    top: 44px;
    height: 580px;
  }
</style>

作业:完善当前文集下的文章列表的BUG

当前文集如果默认是0,那么在用户新建文件以后,显示的文章列表错误的。

所以,我们需要在用户创建文集以后,设置当前文章列表为空列表。

添加文章

视图代码:

from rest_framework.generics import ListAPIView
from .serializers import ArticleModelSerializer
from .models import Article
from rest_framework.response import Response
from rest_framework import status
class ArticleOfCollectionAPIView(ListAPIView, CreateAPIView):
    """文集下的文章"""
    serializer_class = ArticleModelSerializer
    permission_classes = [IsAuthenticated]  # 必须是登陆用户才能访问过来

    def list(self, request,  *args, **kwargs):
        user = request.user
        collection_id = request.query_params.get("collection_id")
        try:
            ArticleCollection.objects.get(pk=collection_id)
        except ArticleCollection.DoesNotExist:
            return Response({"message":"错误的文集ID"}, status=status.HTTP_400_BAD_REQUEST)

        queryset = Article.objects.filter(user=user,collection_id=collection_id).order_by("orders", "-id")
        queryset = self.filter_queryset(queryset)
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

序列化器,代码:

from .models import Article
class ArticleModelSerializer(serializers.ModelSerializer):
    """文章模型序列化器"""
    position = serializers.IntegerField(write_only=True, default=0, allow_null=True, label="添加文章的位置", help_text="在文章列表的前面插入添加则为0, 在文章列表的后面追加添加则为1")
    class Meta:
        model = Article
        fields = ["id","position","name","content","html_content","collection","pub_date","is_public"]
        read_only_fields = ["id","content","html_content","pub_date","is_public"]

    def validate(self, data):
        name = data.get("name")
        if len(name)<1:
            raise serializers.ValidationError("对不起,文章标题不能为空!")

        return data

    def create(self, validated_data):
        """添加文章"""
        try:
            article = Article.objects.create(
                name=validated_data.get("name"),
                user=self.context["request"].user,
                content="",
                html_content="",
                collection=validated_data.get("collection"),
                is_public=False,
                is_show=True,
                orders=0
            )
        except:
            raise serializers.ValidationError("对不起,文章添加失败!")

        if validated_data.get("position"):
            """
            如果用户设置在文章列表后面追加添加文章,则让文章的排序值跟着id来变化越来越大
            排序值越大的文章,越往后排列
            """
            article.orders = article.id
            article.save()

        return article

路由代码与上面的文集的文章列表共用一个路由。

客户端发送ajax,代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>

文章发布

默认情况下,我们的文章属于未发布状态下的,这时候除了作者本人和后台管理员,其他人看不到的。

  1. 在写文章页面,我们需要给用户区分哪些文章已经发布了,哪些文章没有发布,在前面的文章列表中我们已经完成了这个功能。
  2. 在写文章页面,提供一个api,用于修改发布状态。同时,需要提供发布后的文章页面。

切换文章的发布状态

服务端提供切换文章发布状态的功能。、

视图代码:

from rest_framework.views import APIView
class ArticleAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def patch(self,request,pk):
        """切换文章的发布状态"""
        try:
            article = Article.objects.get(user=request.user,pk=pk)
        except Article.DoesNotExist:
            return Response({"message":"当前文章不存在!"}, status=status.HTTP_400_BAD_REQUEST)

        article.is_public = not article.is_public
        article.save()

        return Response(status=status.HTTP_200_OK)

路由代码:

from django.urls import path,re_path
from . import views
urlpatterns = [
    path("collection/", views.CollecionAPIView.as_view()),
    re_path("^collection/(?P<pk>\d+)/$", views.CollecionAPIView.as_view()),
    path("collection/article/", views.ArticleOfCollectionAPIView.as_view()),
    re_path("^public/(?P<pk>\d+)/$", views.ArticleAPIView.as_view()),
]

前端代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public"><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public"><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>取消发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          pub_article(){
              // 切换文章的发布状态
              let article = this.article_list[this.current_article];
              this.$axios.patch(`${this.$settings.Host}/article/public/${article.id}/`,{},{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  // 切换成功
                  article.is_public=!article.is_public;
                  // 关闭菜单
                  this.is_show_article_menu = false;
              }).catch(error=>{
                  this.$message.error("切换文章发布状态失败!");
              })
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>

移动文章

首先修复文章菜单中的文集列表,在css代码中这个位置放置以下代码:

._2_WAp ._2KzJx, ._2_WAp ._3x4X_ {
    position: absolute;
    right: 100%;
    top: 0;
    display: none;
}
._2_WAp:hover ._2KzJx, ._2_WAp:hover ._3x4X_ {
    display: block;
}


服务端提供修改文章的文集ID的接口,在上面切换文章发布状态的视图类中,增加代码:

from rest_framework.views import APIView
class ArticleAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def patch(self,request,pk):
        """切换文章的发布状态"""
        try:
            article = Article.objects.get(user=request.user,pk=pk)
        except Article.DoesNotExist:
            return Response({"message":"当前文章不存在!"}, status=status.HTTP_400_BAD_REQUEST)

        article.is_public = not article.is_public
        article.save()

        return Response(status=status.HTTP_200_OK)

    def put(self,request,pk):
        """移动文章"""
        try:
            article = Article.objects.get(user=request.user, pk=pk)
        except Article.DoesNotExist:
            return Response({"message": "当前文章不存在!"}, status=status.HTTP_400_BAD_REQUEST)

        collection_id = request.data.get("collection_id")
        try:
            collection = ArticleCollection.objects.get(user=request.user, pk=collection_id)
        except ArticleCollection.DoesNotExist:
            return Response({"message": "当前文集不存在!"}, status=status.HTTP_400_BAD_REQUEST)

        article.collection = collection
        article.save()

        return Response(status=status.HTTP_200_OK)

路由与上面发布文章状态切换接口一致。客户端实现切换文集代码效果:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public"><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public"><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>取消发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list" @click.stop="move_article(collection.id)"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          pub_article(){
              // 切换文章的发布状态
              let article = this.article_list[this.current_article];
              this.$axios.patch(`${this.$settings.Host}/article/public/${article.id}/`,{},{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  // 切换成功
                  article.is_public=!article.is_public;
                  // 关闭菜单
                  this.is_show_article_menu = false;
              }).catch(error=>{
                  this.$message.error("切换文章发布状态失败!");
              })
          },
          move_article(collection_id){
              // 移动文章
              this.is_show_article_menu = false;
              let article = this.article_list[this.current_article];
              this.$axios.put(`${this.$settings.Host}/article/public/${article.id}/`,{
                  collection_id, // collection_id: collection_id的简写,
              },{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  this.article_list.splice(this.current_article,1);
              }).catch(error=>{
                  this.$message.error("移动文章失败!");
              });
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>

定时发布[扩展知识点]

原理:使用celery完成定时任务!
步骤:
1. 当用户点选了定时发布, 页面中弹出一个选择时间的窗口。
2. 当用户设置完成发布时间以后,点击“确认”以后,把这个时间和文章id发送到服务端。
3. 服务端中文章模型的pub_date记录这个定时发布时间。
4. 在celery中创建一个定时任务,在每个固定时间段,检查文章表中,对应时间段的pub_date把对应的文章进行发布。

前端增加一个选择时间的弹窗,代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public" @click.stop="is_show_interval_windows=true;is_show_article_menu=false;"><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!article.is_public"><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="article.is_public" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>取消发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list" @click.stop="move_article(collection.id)"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
    <!-- 定时发布弹窗 -->
    <div class="interval_box" v-if="is_show_interval_windows">
      <transition name="el-fade-in-linear">
        <div class="transition-box">
          <div class="_2tIvb">
            <div class="ZTNas" aria-label="Close" @click.stop="is_show_interval_windows=false"><i class="fa fa-close"></i></div>
            <div class="_1KgC3">
              <div class="-K8Re">定时发文 </div>
            </div>
            <div class="_1LROK PWCkH">
              <div class="wzwGh">选择定时发布的时间:</div>
              <div class="On4jq">
                  <el-date-picker
                  v-model="pub_date"
                  type="datetime"
                  format="yyyy-MM-dd HH:mm"
                  placeholder="选择日期时间">
                </el-date-picker>
              </div>
              <div class="_3mEYS">本文章将于<span class="yfqan">{{timeformat(pub_date)}}</span>发布。
              </div>
            </div>
            <div class="RzhZ5 pCffa">
              <button type="button" class="_3zXcJ" @click.stop="is_show_interval_windows=false;"><span>取 消</span></button>
              <button type="button" class="_3zXcJ _3QfkW" @click.stop="interval_article"><span>确 认</span></button>
            </div>
          </div>

        </div>
      </transition>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
                is_show_interval_windows: false, // 选择定时发布文章的时间窗口
                pub_date: new Date().toLocaleDateString(),  // 定时发布当前文章的时间默认值为当前时间
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          interval_article(){
              // 定时发布文章
              let pub_date = this.pub_date;
              let article_id = this.article_list[this.current_article].id;
              // 发送ajax
              
          },
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          timeformat(time){
              // 获取客户端日期时间格式
              time = new Date(time);
              return `${time.getFullYear()}-${time.getMonth()+1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}`;
          },
          pub_article(){
              // 切换文章的发布状态
              let article = this.article_list[this.current_article];
              this.$axios.patch(`${this.$settings.Host}/article/public/${article.id}/`,{},{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  // 切换成功
                  article.is_public=!article.is_public;
                  // 关闭菜单
                  this.is_show_article_menu = false;
              }).catch(error=>{
                  this.$message.error("切换文章发布状态失败!");
              })
          },
          move_article(collection_id){
              // 移动文章
              this.is_show_article_menu = false;
              let article = this.article_list[this.current_article];
              this.$axios.put(`${this.$settings.Host}/article/public/${article.id}/`,{
                  collection_id, // collection_id: collection_id的简写,
              },{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  this.article_list.splice(this.current_article,1);
              }).catch(error=>{
                  this.$message.error("移动文章失败!");
              });
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>


<style scoped>
  /* 页面原来的样式不要删除 */
  /* 新增以下定时发布文章的相关样式 */

    .interval_box{
      display: flex;
      height: 280px;
      width: 400px;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      margin: auto;
      background: #fff;
      border-radius: 4px;
      z-index: 2000;
    }
    .NVdZF{display:block}

  ._20JYe {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    background-color: hsla(0, 0%, 100%, .7);
    height: 100%;
    z-index: 1000;
    filter: alpha(opacity=30)
  }

  body.reader-night-mode ._20JYe {
    background-color: hsla(0, 0%, 40%, .7)
  }

  ._3WS2u {
    display: none
  }

  ._23VW8 {
    position: fixed;
    overflow: auto;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1010;
    -webkit-overflow-scrolling: touch;
    outline: 0
  }

  ._3cgNz {
    position: relative;
    background-color: #fff;
    width: auto;
    border: 0;
    border-radius: 6px;
    margin: 0 auto 24px;
    background-clip: padding-box;
    -webkit-box-shadow: 0 2px 8px rgba(0, 0, 0, .2);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .2)
  }

  body.reader-night-mode ._3cgNz {
    background-color: #3d3d3d
  }

  .cjahm {
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%)
  }

  .cjahm.move-up-appear.move-up-appear-active, .cjahm.move-up-enter.move-up-enter-active {
    -webkit-animation-name: _2oyZp;
    animation-name: _2oyZp
  }

  .cjahm.move-up-leave.move-up-leave-active {
    -webkit-animation-name: _3g8pf;
    animation-name: _3g8pf
  }

  ._2tIvb {
    position: relative;
    border-radius: 6px;
    background-color: #eee;
  }

  body.reader-night-mode ._2tIvb {
    background-color: #3d3d3d
  }

  .ZTNas {
    position: absolute;
    right: 0;
    top: 0;
    width: 48px;
    height: 48px;
    line-height: 46px;
    text-align: center;
    font-size: 16px;
    cursor: pointer;
    color: #999;
    -webkit-transition: color .3s ease;
    -o-transition: color .3s ease;
    transition: color .3s ease
  }

  .ZTNas:hover {
    color: #4d4d4d
  }

  ._1KgC3 {
    padding: 13px 16px;
    border-radius: 4px 4px 0 0;
    color: #595959;
    border-bottom: 1px solid #d9d9d9
  }

  body.reader-night-mode ._1KgC3 {
    border-color: #2e2e2e
  }

  .-K8Re {
    margin: 0;
    font-size: 16px;
    line-height: 21px;
    font-weight: 500;
    color: #4d4d4d
  }

  body.reader-night-mode .-K8Re {
    color: #b3b3b3
  }

  ._3O4M2 {
    font-size: 13px;
    padding-left: 10px;
    color: #999
  }

  ._3O4M2 a {
    color: #3294d0
  }

  .PWCkH {
    padding: 16px;
    font-size: 12px;
    line-height: 1.5;
    color: #595959
  }

  body.reader-night-mode .PWCkH {
    color: #b3b3b3
  }

  .pCffa {
    padding: 0 16px 16px;
    text-align: right;
    border-radius: 0 0 2px 2px
  }

  ._26mjB {
    display: block
  }

  .HhIYk {
    zoom: 1
  }

  .HhIYk:after, .HhIYk:before {
    content: " ";
    display: table
  }

  .HhIYk:after {
    clear: both;
    visibility: hidden;
    font-size: 0;
    height: 0
  }

  ._37SYn {
    font-size: 13px;
    line-height: 20px;
    padding: 30px 30px 20px;
    color: #333
  }

  body.reader-night-mode ._37SYn {
    color: #b3b3b3
  }

  ._2BmdS {
    padding: 0 16px 16px;
    text-align: right;
    border-radius: 0 0 2px 2px
  }

  ._26mjB .PWCkH {
    padding: 0;
    border-radius: 2px
  }

  ._26mjB .PWCkH a {
    color: #3194d0
  }

  ._26mjB .PWCkH a:active, ._26mjB .PWCkH a:hover {
    color: #2b86bc
  }

  ._26mjB ._38pIX {
    padding: 30px 16px;
    border: 0
  }

  ._26mjB ._38pIX input {
    display: block;
    border-radius: 4px;
    width: 100%;
    line-height: 20px;
    padding: 5px 10px;
    font-size: 15px;
    background-color: transparent;
    border: 1px solid #ccc
  }

  body.reader-night-mode ._26mjB ._38pIX input {
    border-color: #2e2e2e
  }

  ._26mjB .ZTNas {
    width: 32px;
    height: 32px;
    line-height: 32px
  }

  .HSpeJ .PWCkH {
    border: 0
  }
</style>

服务端提供修改pub_date发布时间的api接口

配置文件中, 调整市区,代码:

# 必须设置为True,否则后面提交发布时间到服务端会报错!
USE_TZ = True

视图代码:

class ArticleIntervalAPIView(APIView):
    """定时发布文章"""
    permission_classes = [IsAuthenticated]
    def put(self,request,pk):
        try:
            article = Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            return Response("对不起,当前文章不存在!", status=status.HTTP_400_BAD_REQUEST)

        pub_date = request.data.get("pub_date")
        article.pub_date = pub_date
        article.save()
        return Response("操作成功!")

路由,代码:

urlpatterns = [
	
    ....
    
    re_path("^interval/(?P<pk>\d+)/$", views.ArticleIntervalAPIView.as_view()),
]

客户端发送ajax请求,代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public,interval:article.pub_date}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)" @click.stop="is_show_interval_windows=true;is_show_article_menu=false;"><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)"><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="article.is_public||article.pub_date" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>取消发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list" @click.stop="move_article(collection.id)"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
    <!-- 定时发布弹窗 -->
    <div class="interval_box" v-if="is_show_interval_windows">
      <transition name="el-fade-in-linear">
        <div class="transition-box">
          <div class="_2tIvb">
            <div class="ZTNas" aria-label="Close" @click.stop="is_show_interval_windows=false"><i class="fa fa-close"></i></div>
            <div class="_1KgC3">
              <div class="-K8Re">定时发文 </div>
            </div>
            <div class="_1LROK PWCkH">
              <div class="wzwGh">选择定时发布的时间:</div>
              <div class="On4jq">
                  <el-date-picker
                  v-model="pub_date"
                  type="datetime"
                  format="yyyy-MM-dd HH:mm"
                  placeholder="选择日期时间">
                </el-date-picker>
              </div>
              <div class="_3mEYS">本文章将于<span class="yfqan">{{timeformat(pub_date)}}</span>发布。
              </div>
            </div>
            <div class="RzhZ5 pCffa">
              <button type="button" class="_3zXcJ" @click.stop="is_show_interval_windows=false;"><span>取 消</span></button>
              <button type="button" class="_3zXcJ _3QfkW" @click.stop="interval_article"><span>确 认</span></button>
            </div>
          </div>

        </div>
      </transition>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
                is_show_interval_windows: false, // 选择定时发布文章的时间窗口
                pub_date: new Date().toLocaleDateString(),  // 定时发布当前文章的时间默认值为当前时间
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          interval_article(){
              // 定时发布文章
              let article_id = this.article_list[this.current_article].id;
              // 发送ajax
              this.$axios.put(`${this.$settings.Host}/article/interval/${article_id}/`,{
                  pub_date: this.pub_date,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list[this.current_article].pub_date = this.pub_date;
                  this.$message.success("文章设置成功!");
              }).catch(error=>{
                  this.$message.error("操作失败!请联系客服工作人员!");
              });
              this.is_show_interval_windows=false;// 关闭定时窗口
          },
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          timeformat(time){
              // 获取客户端日期时间格式
              time = new Date(time);
              return `${time.getFullYear()}-${time.getMonth()+1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}`;
          },
          pub_article(){
              // 切换文章的发布状态
              let article = this.article_list[this.current_article];
              this.$axios.patch(`${this.$settings.Host}/article/public/${article.id}/`,{},{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  // 切换成功
                  article.is_public=!article.is_public;
                  // 关闭菜单
                  this.is_show_article_menu = false;
              }).catch(error=>{
                  this.$message.error("切换文章发布状态失败!");
              })
          },
          move_article(collection_id){
              // 移动文章
              this.is_show_article_menu = false;
              let article = this.article_list[this.current_article];
              this.$axios.put(`${this.$settings.Host}/article/public/${article.id}/`,{
                  collection_id, // collection_id: collection_id的简写,
              },{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  this.article_list.splice(this.current_article,1);
              }).catch(error=>{
                  this.$message.error("移动文章失败!");
              });
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>
<style>
/*  注意:在素材目录下找到sprite.9d24217.png放到static/image目录中 */
._25Ilv .interval {
    background: url(/static/image/sprite.9d24217.png) no-repeat -74px -25px;
    background-size: 250px;
}
</style>

当用户取消发布时,我们应该把定时发布也取消掉,也就是说把当前文章的pub_date清空!

视图中切换文章发布状态视图接口代码进行调整,代码:

from rest_framework.views import APIView
class ArticleAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def patch(self,request,pk):
        """切换文章的发布状态"""
        try:
            article = Article.objects.get(user=request.user,pk=pk)
        except Article.DoesNotExist:
            return Response({"message":"当前文章不存在!"}, status=status.HTTP_400_BAD_REQUEST)

        # 判断如果是取消发布,则取消定时发布
        # 1. 定时发布
        if article.is_public == False and article.pub_date is not None:
            # 原来定时发布的,现在取消
            article.pub_date = None
        elif article.is_public == False and article.pub_date is None:
            # 原来没有发布的,现在立即发布
            article.is_public = True
        elif article.is_public == True:
            # 原来发布的,现在取消
            article.is_public = False
        article.save()

        return Response(status=status.HTTP_200_OK)

客户端状态在取消发布以后调整显示状态,代码:

<template>
  <div class="write" v-if="is_show_page">
    <div class="_2v5v5">
      <div class="_3zibT"><router-link to="/">回首页</router-link></div>
      <div class="_1iZMb">
        <div class="_33Zlg" @click="collection_form=true"><i class="fa fa-plus"></i><span>新建文集</span></div>
        <div class="_2G97m">
          <form class="M8J6Q" :class="collection_form?'_2a1Rp':'_1mU5v'">
            <input type="text" placeholder="请输入文集名..." v-model="collection_name" class="_1CtV4">
            <button type="submit" class="dwU8Q _3zXcJ _3QfkW" @click.stop.prevent="add_collection"><span>提 交</span></button>
            <button type="button" class="vIzwB _3zXcJ" @click.stop.prevent="collection_form=false"><span>取 消</span></button>
          </form>
        </div>
      </div>
      <ul class="_3MbJ4 _3t059">
        <li class="_3DM7w" :class="{_31PCv:current_collection==key}" @click.stop="current_collection=key;is_show_collection_menu=false;" :title="collection.name" v-for="collection,key in collection_list">
          <div class="_3P4JX _2VLy-" v-if="current_collection==key" @click.stop="is_show_collection_menu=!is_show_collection_menu">
            <i class="fa fa-gear"></i>
            <span>
              <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_collection_menu}">
                <li class="_2po2r cRfUr" title="">
                  <span class="" @click.stop="edit_collection"><i class="fa fa-pencil-square-o _22XWG"></i>修改文集</span>
                </li>
                <li class="_2po2r cRfUr" title="">
                  <span class=""><i class="fa fa-trash-o _22XWG"></i>删除文集</span>
                </li>
              </ul>
            </span>
          </div>
          <span>{{collection.name}}</span>
        </li>
      </ul>
      <div style="height: 50px;"></div>
      <div role="button" class="h-5Am">
        <span class="ant-dropdown-trigger"><i class="fa fa-bars"></i><span>设置</span></span>
        <span class="Yv5Zx">遇到问题<i class="fa fa-question-circle-o"></i></span>
      </div>
    </div>
    <div class="rQQG7">
      <div class="_3revO _2mnPN">
        <div class="_3br9T">
          <div>
            <div class="_1GsW5" @click.stop="add_article(0)"><i class="fa fa-plus-circle"></i><span> 新建文章</span></div>
            <ul class="_2TxA-">
              <li class="_25Ilv" :class="{_33nt7:key==current_article}" @click.stop="current_article=key;" :title="article.name" v-for="article,key in article_list">
                <i class="_13kgp" :class="{_2m93u:article.is_public,interval:article.pub_date}"></i>
                <div class="_3P4JX poOXI" v-if="key==current_article" @click.stop="is_show_article_menu=!is_show_article_menu">
                  <i class="fa fa-gear"></i>
                  <span>
                    <ul class="_2V8zt _3FcHm _2w9pn" :class="{NvfK4:is_show_article_menu}">
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>直接发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)" @click.stop="is_show_interval_windows=true;is_show_article_menu=false;"><span class=""><i class="fa fa-clock-o _22XWG"></i>定时发布</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="!(article.is_public||article.pub_date)"><span class="_20tIi"><i class="iconfont ic-paid _22XWG"></i>发布为付费文章</span></li>
                      <li class="_2po2r cRfUr" title="" v-if="article.is_public||article.pub_date" @click.stop="pub_article"><span class=""><i class="fa fa-share _22XWG"></i>取消发布</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="iconfont ic-set _22XWG"></i>设置发布样式</span></li>
                      <li class="_3nZXj _2_WAp _3df2u _2po2r cRfUr" title=""><span class=""><i class="fa fa-folder-open _22XWG"></i>移动文章
                        <div class="_3x4X_">
                          <ul class="_2KzJx oGKRI _3DXDE _2w9pn">
                            <li class="_2po2r cRfUr" :title="collection.name" v-if="key!=current_collection" v-for="collection,key in collection_list" @click.stop="move_article(collection.id)"><span class="">{{collection.name}}</span></li>
                          </ul>
                        </div>
                      </span>
                      </li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-history _22XWG"></i>历史版本</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-trash-o _22XWG"></i>删除文章</span></li>
                      <li class="_2po2r cRfUr" title=""><span class=""><i class="fa fa-ban _22XWG"></i>设置禁止转载</span></li>
                    </ul>
                  </span>
                </div>
                <span class="NariC">{{article.name}}</span>
                <span class="hLzJv">{{article.content|truncate}}</span>
                <span class="_29C-V" v-if="key==current_article">字数:{{article.content.length}}</span>
              </li>
<!--              <li class="_25Ilv" title="2020-01-12">-->
<!--                <i class="_13kgp"></i>-->
<!--                <span class="NariC">2020-01-12</span>-->
<!--                <span class="hLzJv">题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?-->

<!--题目:企业发放的奖金根据利润提成</span>-->
<!--              </li>-->
            </ul>
            <div class="_2cVn3" @click.stop="add_article(1)"><i class="fa fa-plus"></i><span> 在下方新建文章</span></div>
          </div>
        </div>
      </div>
      <input type="text" class="_24i7u" value="2020-01-12">
      <div id="editor">
        <mavon-editor
          style="height: 100%"
          v-model="editorContent"
          :ishljs="true"
          ref=md
          @imgAdd="imgAdd"
          @imgDel="imgDel"
        ></mavon-editor>
      </div>
    </div>
    <!-- 定时发布弹窗 -->
    <div class="interval_box" v-if="is_show_interval_windows">
      <transition name="el-fade-in-linear">
        <div class="transition-box">
          <div class="_2tIvb">
            <div class="ZTNas" aria-label="Close" @click.stop="is_show_interval_windows=false"><i class="fa fa-close"></i></div>
            <div class="_1KgC3">
              <div class="-K8Re">定时发文 </div>
            </div>
            <div class="_1LROK PWCkH">
              <div class="wzwGh">选择定时发布的时间:</div>
              <div class="On4jq">
                  <el-date-picker
                  v-model="pub_date"
                  type="datetime"
                  format="yyyy-MM-dd HH:mm"
                  placeholder="选择日期时间">
                </el-date-picker>
              </div>
              <div class="_3mEYS">本文章将于<span class="yfqan">{{timeformat(pub_date)}}</span>发布。
              </div>
            </div>
            <div class="RzhZ5 pCffa">
              <button type="button" class="_3zXcJ" @click.stop="is_show_interval_windows=false;"><span>取 消</span></button>
              <button type="button" class="_3zXcJ _3QfkW" @click.stop="interval_article"><span>确 认</span></button>
            </div>
          </div>

        </div>
      </transition>
    </div>
  </div>
</template>
<script>
    import { mavonEditor } from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    import "../../static/font-awesome/css/font-awesome.css";
    export default {
        name: "Writer",
        data(){
            return {
                is_show_page: false, // 是否显示页面
                collection_list:[],  // 文集列表
                article_list:[],     // 文章列表
                current_collection: 0, // 当前选中的文集下标,默认为0
                current_article:0,     // 当前选中的文章下标,默认为0
                editorContent:"",
                img_file:[],
                collection_form:false,
                collection_name:"",
                is_show_collection_menu: false, // 是否显示文集的菜单
                is_show_article_menu: false, // 是否显示文章的菜单
                position: 0,  // 添加文章的位置,默认前面插入,值为0
                is_show_interval_windows: false, // 选择定时发布文章的时间窗口
                pub_date: new Date().toLocaleDateString(),  // 定时发布当前文章的时间默认值为当前时间
            }
        },
        filters:{
           truncate(content){
               if(content === null){
                   return 0;
               }
               return content.substr(0,30);
           }
        },
        watch:{
            editorContent(){
                console.log(this.editorContent);
            },
            current_collection(){
                // 切换文集
                this.get_article_of_collection();
                // 关闭文章菜单
                this.is_show_article_menu = false;
            },
            current_article(){
                // 切换文章
                this.is_show_article_menu = false;
            }
        },
        created(){
            // 判断登录
            this.$settings.check_user_login(this,"警告","您尚未登录!", "跳转到登录", "/login");
            if(this.token){
                // 显示页面
                this.is_show_page = true;
                // 获取当前用户的文集列表
                this.get_collection();
            }
        },
        mounted(){
            if(this.is_show_page){
                document.querySelector("#editor").style.height = document.documentElement.clientHeight - document.querySelector("._24i7u").clientHeight + "px";
                // 点选页面其他位置,关闭菜单
                document.onclick = (event)=>{
                    // 关闭文集菜单
                    this.is_show_collection_menu = false;
                    // 关闭文章菜单
                    this.is_show_article_menu = false;
                }
            }
        },
        components: {
          mavonEditor
        },
        methods:{
          interval_article(){
              // 定时发布文章
              let article_id = this.article_list[this.current_article].id;
              // 发送ajax
              this.$axios.put(`${this.$settings.Host}/article/interval/${article_id}/`,{
                  pub_date: this.pub_date,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list[this.current_article].pub_date = this.pub_date;
                  this.$message.success("文章设置成功!");
              }).catch(error=>{
                  this.$message.error("操作失败!请联系客服工作人员!");
              });
              this.is_show_interval_windows=false;// 关闭定时窗口
          },
          edit_collection(){
              // 修改文集
              this.is_show_collection_menu=false; // 关闭文集的操作菜单
              this.$prompt('请输入新文集名', '提示', {
                confirmButtonText: '保存',
                cancelButtonText: '取消',
                inputPattern: /.{1,}/,
                inputErrorMessage: '文集名称不能为空!',
                inputValue: this.collection_list[this.current_collection].name,
              }).then(({ value }) => {
                 // 点击确定,需要把当前文集名称提交到服务端进行修改
                 let collection_id = this.collection_list[this.current_collection].id;
                 this.$axios.put(`${this.$settings.Host}/article/collection/${collection_id}/`,{
                     name: value,
                 },{
                     headers:{
                         Authorization:"jwt " + this.token,
                     }
                 }).then(response=>{
                     // 服务端ajax请求操作成功,则客户端的name也要发生改变
                     this.collection_list[this.current_collection].name = value;
                 }).catch(error=>{
                     this.$message.error(error.response.data);
                 })
              }).catch(() => {

              });
          },
          add_collection(){
              // 添加文集
              if(this.collection_name.length<1){
                  this.$message.error("文集名称不能为空!");
                  return;
              }
              // 发送ajax请求
              this.$axios.post(`${this.$settings.Host}/article/collection/`,{
                  name: this.collection_name
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("添加文集成功!");
                  this.collection_name = "";
                  this.collection_form = false; // 隐藏添加文集的表单
                  // 把服务端中添加返回的文集信息,保存到collection_list中
                  this.collection_list.unshift(response.data);
              }).catch(error=>{
                  this.$message.error(error.response.data);
              });
          },
          get_collection(){
              // 获取用户的文集列表
              this.$axios.get(`${this.$settings.Host}/article/collection/`,{
                  headers:{
                      Authorization: "jwt " + this.token, // 必须在左边加上 "jwt ",空格!!!
                  }
              }).then(response=>{
                  this.collection_list = response.data;
                  // 获取当前选中文集的文章列表
                  this.get_article_of_collection();
              }).catch(error=>{
                  this.$message.error("对不起,无法获取当前用户的文集列表!");
              });
          },
          get_article_of_collection(){
              // 获取当前文集的文章列表
              this.$axios.get(`${this.$settings.Host}/article/collection/article/`,{
                  params:{
                      collection_id: this.collection_list[this.current_collection].id,
                  },
                  headers:{
                     Authorization:"jwt " + this.token,
                  }
              }).then(response=>{
                  this.article_list = response.data;
              }).catch(error=>{
                  this.$message.error(error.response.data.message);
              })
          },
          add_article(position){
              // 添加文章

              this.$axios.post(`${this.$settings.Host}/article/collection/article/`,{
                  collection: this.collection_list[this.current_collection].id,
                  name: this.get_datetime(),
                  position: position,
              },{
                  headers:{
                      Authorization: "jwt " + this.token,
                  }
              }).then(response=>{
                  this.$message.success("文章添加成功!");
                  // 给本地的aticle追加/插入新建的文章
                  if(position){
                      // 追加
                      this.article_list.push(response.data);
                  }else{
                      // 插入
                      this.article_list.unshift(response.data);
                  }
              }).catch(error=>{
                  this.$message.error("对不起,添加文章失败!");
              });
          },
          get_datetime(){
              // 获取客户端日期格式
              let datetime = new Date();
              let Y = datetime.getFullYear();
              let m = datetime.getMonth()+1;
              m = m<10?"0"+m:m;
              let d = datetime.getDate();
              d = d<10?"0"+d:d;
              return `${Y}-${m}-${d}`;
          },
          timeformat(time){
              // 获取客户端日期时间格式
              time = new Date(time);
              return `${time.getFullYear()}-${time.getMonth()+1}-${time.getDate()} ${time.getHours()}:${time.getMinutes()}`;
          },
          pub_article(){
              // 切换文章的发布状态
              let article = this.article_list[this.current_article];
              this.$axios.patch(`${this.$settings.Host}/article/public/${article.id}/`,{},{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  // 切换成功
                  if(article.pub_date || article.is_public){
                      // 取消发布
                      article.is_public=false;
                      article.pub_date=null;
                  }else{
                      // 立即发布
                      article.is_public=true;
                  }

                  // 关闭菜单
                  this.is_show_article_menu = false;
              }).catch(error=>{
                  this.$message.error("切换文章发布状态失败!");
              })
          },
          move_article(collection_id){
              // 移动文章
              this.is_show_article_menu = false;
              let article = this.article_list[this.current_article];
              this.$axios.put(`${this.$settings.Host}/article/public/${article.id}/`,{
                  collection_id, // collection_id: collection_id的简写,
              },{
                  headers:{
                      Authorization: "jwt "+this.token,
                  }
              }).then(response=>{
                  this.article_list.splice(this.current_article,1);
              }).catch(error=>{
                  this.$message.error("移动文章失败!");
              });
          },
          // 绑定@imgAdd event
          imgAdd(pos, $file){
              // 添加文件

          },
          imgDel(pos) {
              // 删除文件

          }
        }
    }
</script>

celery的定时任务,每分钟执行一次定时发布操作,让pub_date时间到了,则更新对应文章的发布状态。

在mycelery中创建article任务目录,在目录下创建任务文件tasks.py,编写异步任务:

from mycelery.main import app
from article.models import Article
from datetime import datetime
@app.task(name="interval_pub_article")
def interval_pub_article():
    """定时发布文章"""
    article_list = Article.objects.exclude(pub_date=None)
    for article in article_list:
        pub_date_timestamp = int(article.pub_date.timestamp())
        current_timestamp = int(datetime.now().timestamp() +8 * 60 * 60)
        if pub_date_timestamp <= current_timestamp:
            article.pub_date = None
            article.is_public = True
            article.save()

注册异步任务到main.py中。并重启celery,代码:

from celery import Celery
# 初始化celery对象
app = Celery("renran")

# 初始化django
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'renranapi.settings.dev')
import django
django.setup()

# 加载配置
app.config_from_object("mycelery.config")

# 注册异步任务
# 任务以包进行管理,每一个包必须里面包含了一个tasks.py文件
app.autodiscover_tasks(["mycelery.sms","mycelery.article"])

# 在终端下面运行celery,将来在线上服务器中,可以使用supervisor以守护进程的方式启动
# celery -A mycelery.main worker --loglevel=info

接下来,我们就可以使用celery的定时任务调度器,让celery定时执行异步任务。

Celery官方文档中关于定时任务使用的说明:

http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html

在mycelery中config.py配置异步任务定时执行,代码:

# 任务队列的链接地址
broker_url = 'redis://127.0.0.1:6379/15'
# 结果队列的链接地址
result_backend = 'redis://127.0.0.1:6379/14'

# 定时任务调度器相关配置
from .main import app
from celery.schedules import crontab
app.conf.beat_schedule = {
    # 定时任务列表
    'pub-article-every-one-minute': {
        'task': 'interval_pub_article', # 指定定时执行的的异步任务
        # 'schedule': crontab(),         # 时间间隔,一分钟
        'schedule': 30.0,               # 时间间隔,默认:秒
        # 'args': (16, 16)              # 如果任务有固定参数,则可以写在args
    },
}

# 和django框架同步时区
from django.conf import settings
app.conf.timezone = settings.TIME_ZONE

# 完成上面的配置以后,重启celery并在新的终端窗口执行命令,运行定时任务的调度器
# celery -A mycelery.main beat  # mycelery.main 是celery的主应用文件

接下来,我们就可以重启Celery并启用Celery的定时任务调度器

先在终端下,运行celery的定时任务程序,以下命令:

celery -A mycelery.main beat  # mycelery.main 是celery的主应用文件

然后再新建一个终端,运行以下命令,上面的命令必须先指定:

celery -A mycelery.main worker --loglevel=info

注意,使用的时候,如果有时区必须先配置好系统时区。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值