首页 > 编程语言 >VUE.JS和NODE.JS构建一个简易的前后端分离静态博客系统(三)

VUE.JS和NODE.JS构建一个简易的前后端分离静态博客系统(三)

时间:2023-04-13 12:56:45浏览次数:48  
标签:NODE VUE title JS axios 10px 组件 post data

Edit.vue

<template>
  <div id="edit">
    <ClassicHeader>
      <template v-slot:left>
        <span>编辑随笔</span> 
      </template>
      <template v-slot:right>
        <el-button @click="newPost('Management')">存为草稿</el-button> 
        <el-button @click="newPost()">存为草稿并继续编辑</el-button>   
      </template>
    </ClassicHeader>
    <main>
      <div id="post_title">
        <el-input v-model="post_title" placeholder="标题"></el-input>
      </div>
      <div id="post_content">       
        <editor :init="tinymce_init" v-model="post_content" />
      </div>
      <div id="post_category">
        <SubTitle>分类</SubTitle>  
        <el-checkbox-group v-model="checkList">
          <el-checkbox v-for="category in categories" :key="category.id" :label="category.id">{{category.name}}</el-checkbox>
        </el-checkbox-group>
      </div>      
    </main>
  </div>
</template>

<script>
import axios from 'axios'
import qs from 'qs'
import Editor from '@tinymce/tinymce-vue'
import ClassicHeader from '@/components/ClassicHeader.vue'
import SubTitle from '@/components/SubTitle'

export default {
  name: 'Edit',
  components: {
    'editor': Editor,
    ClassicHeader,
    SubTitle,
  },
  data() {
    return {
      post_title: '',
      post_content: '',
      checkList: [],
      tinymce_init: {
        height: 654,
        language: 'zh-Hans',
        menubar: false,
        plugins: 'wordcount table searchreplace save preview media lists link insertdatetime image emoticons codesample code charmap autolink anchor advlist',
        toolbar: ['bold italic underline strikethrough | numlist bullist | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | searchreplace | preview wordcount | print',
          'link unlink anchor | removeformat | codesample | code | blocks fontfamily | image media insertdatetime insertfile emoticons charmap  | undo redo',
          'table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol'],
      },
      categories: [],
    }
  },
  mounted() {
    this.reloadCategories()
  },
  methods: {
    reloadCategories() {
      axios.get(this.$url_categories)
        .then(resp => {         
          this.categories = resp.data
        })
        .catch(err => {
          console.log(err)
        })       
    },
    newPost(jump2 = 'Edit2') {
      if (!this.post_title && this.post_title.trim() === '') {
        this.$message.error('标题不能为空!');
        return
      }
      console.log(this.checkList)
      const DATA = qs.stringify({
        'title': this.post_title,
        'content': this.post_content,
        'category': JSON.stringify(this.checkList),
      });
      const CONFIG = {
        method: 'post',
        url: this.$url_posts,
        headers: { 
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        data : DATA
      };
      // 把屏幕锁了防止乱点
      const LOADING = this.$loading({
        lock: true,
        // spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      });  
      axios(CONFIG)
      .then((resp) => {
        let post = resp.data                        
        setTimeout(() => {
          // 至少锁1秒才解除
          LOADING.close();
          this.$message({
            type: 'success',
            message: '操作成功',
          });  

          if (this.$route.name !== jump2) {
            this.$router.push({ 
              name: jump2,
              query: {
                post_id: post.id,
              },
            });
          }  

        }, 1000);                         
      })
      .catch(function (error) {
        console.log(error);
      });
    }
  },
}
</script>

<style scoped>
  #edit {
    display: flex;
    flex-direction: column;
  }

  main {
    display: flex;
    flex-direction: column;
    
    padding-bottom: 30px;
  }

  #post_title {
    padding: 10px 10px;
    border: 1px solid #dcdfe6; 
  }

  #post_content {
    padding: 10px 10px;
    border: 1px solid #dcdfe6;     
  }

  #post_category {
    padding: 10px 10px;
    border: 1px solid #dcdfe6;     
  }
</style>

这段代码也是一个Vue.js组件,实现了一个新建文章的编辑页面。页面包含一个标题、一个内容编辑器和一个分类复选框,以及两个按钮:存为草稿、存为草稿并继续编辑。

与前一个组件不同的是,该组件的post属性被拆分成了post_title和post_content两个变量,用于分别存储文章的标题和内容。另外,该组件中没有定义post的状态属性。相应地,该组件的方法中也没有定义发布文章的方法,只有一个newPost()方法,用于将用户编辑的文章保存为草稿。

该组件的实现与前一个组件的大部分代码类似,只是去除了一些发布文章相关的逻辑,而增加了一些保存草稿相关的逻辑。在newPost()方法中,用户编辑的文章数据被包装成一个表单数据,通过axios发送到服务器进行保存。保存成功后,根据不同的参数值,分别跳转到编辑页面或文章管理页面。

最后,该组件也定义了一些样式,用于控制页面的布局和样式,其中#edit用于设置页面的显示方式为flex布局,#post_title、#post_content和#post_category用于设置标题、内容编辑器和分类复选框的边框和内边距。这些样式是通过Vue.js的scoped样式功能实现的,只对当前组件的DOM元素生效,不影响全局样式。

Edit2.vue

<template>
  <div id="edit2">
    <ClassicHeader>
      <template v-slot:left>
        <span>正在编辑随笔:{{ post_id }}</span>
      </template>
      <template v-slot:right>
        <el-button @click="publish" :disabled="post.state === '已发布'">发布</el-button>
        <el-button @click="updatePost()">保存</el-button>
        <el-button @click="updateAndExit()">保存并退出编辑模式</el-button>
      </template>
    </ClassicHeader>
    <main>
      <div id="post_title">
        <el-input v-model="post.title" placeholder="标题"></el-input>
      </div>
      <div id="post_content">
        <editor :init="tinymce_init" v-model="post.content" />
      </div>
      <div id="post_category">
        <SubTitle>分类</SubTitle>  
        <el-checkbox-group v-model="checkList">
          <el-checkbox v-for="category in categories" :key="category.id" :label="category.id">{{category.name}}</el-checkbox>
        </el-checkbox-group>
      </div>      
    </main>
  </div>
</template>

<script>
import axios from 'axios'
import qs from 'qs'
import Editor from '@tinymce/tinymce-vue'
import ClassicHeader from '@/components/ClassicHeader.vue'
import SubTitle from '@/components/SubTitle'

export default {
  name: 'Edit2',
  components: {
    'editor': Editor,
    ClassicHeader,
    SubTitle,
  },
  data() {
    return {
      post: {
        title: '',
        content: '',
        state: '',
      },
      categories: [],
      checkList: [],
      tinymce_init: {
        height: 654,
        language: 'zh-Hans',
        menubar: false,
        plugins: 'wordcount table searchreplace save preview media lists link insertdatetime image emoticons codesample code charmap autolink anchor advlist',
        toolbar: ['bold italic underline strikethrough | numlist bullist | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | searchreplace | preview wordcount | print',
          'link unlink anchor | removeformat | codesample | code | blocks fontfamily | image media insertdatetime insertfile emoticons charmap  | undo redo',
          'table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol'],
      },
    }
  },
  computed: {
    post_id: {
      get() {
        return this.$route.query.post_id
      },
    }
  },
  mounted() {
    this.reloadCategories()
    this.reloadPost()        
  },
  methods: {
    reloadPost() {
      axios.get(this.$url_posts + `/${ this.post_id }`)
      .then(resp => {  
          this.post = resp.data
          // 不懂为啥,对于数组不管是发送还是接收都要手动进行 解析/转化
          this.checkList = JSON.parse(this.post.category)   
      })
      .catch(err => {
          console.log(err)
      }) 
    },
    reloadCategories() {
      axios.get(this.$url_categories)
        .then(resp => {         
          this.categories = resp.data
        })
        .catch(err => {
          console.log(err)
        })       
    },
    publish() {
      this.post.state = '已发布'
      this.post.pubDate = new Date()
      this.updateAndExit()
    },
    updateAndExit() {
      this.updatePost(true)
    },
    updatePost(jump2 = false) {
      if (!this.post.title && this.post.title.trim() === '') {
        this.$message.error('标题不能为空!');
        return
      }

      const data = qs.stringify({
        'title': this.post.title,
        'content': this.post.content,
        'state': this.post.state,
        'pubDate': this.post.pubDate,
        'category': JSON.stringify(this.checkList),
      });

      const config = {
        method: 'post',
        url: this.$url_posts + `/${this.post_id}`,
        headers: { 
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        data : data
      };

      const LOADING = this.$loading({
        lock: true,
        background: 'rgba(0, 0, 0, 0.7)'
      });  

      axios(config)
      .then((resp) => {
        let post = resp.data    
        console.log(post)  

        setTimeout(() => {
          LOADING.close();
          this.$message({
            type: 'success',
            message: '操作成功',
          });  

          if (jump2) {
            if (this.$route.name !== 'Management') {
              this.$router.push({ 
                name: 'Management',
              });
            }  
          }
        }, 1000);                         
      })
      .catch(function (error) {
        console.log(error);
      });
    },    
  },
}
</script>

<style scoped>
  #edit2 {
    display: flex;
    flex-direction: column;

    padding-bottom: 30px;
  }

  #post_title {
    padding: 10px 10px;
    border: 1px solid #dcdfe6; 
  }

  #post_content {
    padding: 10px 10px;
    border: 1px solid #dcdfe6;     
  }

  #post_category {
    padding: 10px 10px;
    border: 1px solid #dcdfe6;     
  }
</style>

这段代码是一个Vue.js组件,主要实现了一个编辑页面的功能。页面包含一个标题、一个内容编辑器、一个分类复选框和三个按钮:发布、保存、保存并退出编辑模式。

该组件首先引入了一些第三方库和组件,如axios(一个基于Promise的HTTP库)、qs(一个处理URL参数和请求payload的库)、@tinymce/tinymce-vue(一个Vue.js组件,提供了一个所见即所得的富文本编辑器)以及一些自定义组件。接着定义了一些组件内部的data和computed属性,用于存储页面的状态和一些计算属性。其中post属性用于存储用户编辑的文章的标题、内容和状态,categories属性用于存储文章分类列表,checkList属性用于存储用户选择的分类,tinymce_init属性用于配置富文本编辑器的一些参数,post_id属性用于获取路由参数中的post_id参数。

该组件定义了一些方法,用于加载文章和分类、发布文章、保存文章和退出编辑模式。其中reloadPost()和reloadCategories()方法用于加载文章和分类列表,publish()方法用于将文章状态设置为已发布,并调用updateAndExit()方法保存并退出编辑模式,updateAndExit()方法用于保存文章并退出编辑模式,updatePost()方法用于保存文章的具体实现。该方法首先对文章标题进行非空校验,然后将用户编辑的文章数据包装成一个表单数据,通过axios发送到服务器进行保存,并在保存成功后显示一个操作成功的提示消息。

最后,该组件还定义了一些样式,用于控制页面的布局和样式。其中#edit2用于设置页面的显示方式为flex布局,#post_title、#post_content和#post_category用于设置标题、内容编辑器和分类复选框的边框和内边距。这些样式是通过Vue.js的scoped样式功能实现的,只对当前组件的DOM元素生效,不影响全局样式。

标签:NODE,VUE,title,JS,axios,10px,组件,post,data
From: https://www.cnblogs.com/xkxf/p/17314324.html

相关文章

  • 面试题:JS如何最快的执行垃圾回收机制
    因为没看见答案,所以也不知道对不对。 JavaScript的垃圾回收机制是由JavaScript引擎自动管理的,通常情况下我们无法控制垃圾回收机制的执行时间和频率。然而,我们可以采取一些优化策略来减少垃圾回收的性能开销,从而提高代码执行速度。减少全局变量:全局变量不易被垃圾回收,因......
  • mvc响应-返回json数据
         ......
  • JS倒计时效果
    倒计时实现思路:1-用户输入的时间减去当前的时间就是剩余的时间,即倒计时2-不能用时分秒直接相减,因为出来的结果会是负数,比如01分减去15分,结果是负14分3-需要引入时间戳的概念,用户输入时间的总毫秒数减去当前时间的总毫秒数,得到的就是剩余时间的毫秒数4-最后把剩余时间的毫秒数转化......
  • Nuxtjs实现服务端渲染和静态化站点
    本文将介绍如何使用Nuxtjs对vue项目进行ssr和静态化处理。Nuxtjs简单介绍首先,我们简单了解下Nuxtjs框架,Nuxt.js是一个基于Vue的通用框架,主要用于解决Vue项目的服务端渲染(SSR)。它本质上是一个Vue框架,增加一层node服务,通过对客户端/服务端的抽象封装,使用Nuxt各种资源配置,处理服......
  • VUE.JS和NODE.JS构建一个简易的前后端分离静态博客系统(二)
    后台管理页面,需要配合NODE.JS搭建的EXPRESS服务器使用。main.jsimportVuefrom'vue'importAppfrom'./App.vue'importrouterfrom'./router'import{Button,Input,Form,Link,Divider,Upload,Dialog,Card,Popover,Messa......
  • js中无需点击就可以实现页面跳转
    js中无需点击就可以实现页面跳转第一种:复制代码代码如下:<scriptlanguage="javascript"type="text/javascript">window.location.href="xx.jsp?backurl="+window.location.href;</script>第二种:复制代码代码如下:<scriptlanguage="javascript......
  • js 计算时间范围的时间差(只计算工作日,不计算周六周日,精确到天)
    直接上demo代码和截图btnClick(){ varoneDay=1000*60*60*24; vardays=0; //dates是一个时间范围,startDate是时间范围的开始时间,endDate是结束时间 varstartDate=this.dates[0]; varendDate=this.dates[1]; if(endDate.getTime()>0&&startDate.g......
  • Vue.js 独享路由守卫
    视频独享守卫:beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){//判断当前路由是否需要进行权限控制 if(localStorage.getItem('school')==='atguigu'){ next() }else{ alert('暂无权限查看') //next(......
  • 【BUG】ExtJS 的Tab Reorder 插件持续更新布局问题解决办法 (Solution to layout issue
    更新记录2023年4月13日初始化。ExtJS教程汇总:https://www.cnblogs.com/cqpanda/p/16328016.html问题不停的拖动tab栏,会不断更新布局。Draggingthetabbarcontinuouslywillupdatethelayoutconstantly.解决办法进入ExtJS包,打开ux目录下的BoxReorderer.js文件,找......
  • Vuex module之间调用与读值
    对于模块内部的mutation和getter,接收的第一个参数是模块的局部状态对象state对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState使用全局state和getter,rootState和rootGetters会作为第三和第四参数传入getter,也会通过conte......