[记录四] Vue(全家桶)+node+koa2+mysql+nginx+redis,博客全栈项目之web前端页面完结和路由的编写

导语:

暑假在家闲着无事,就琢磨着做一个web博客练练手,现在已经做完了,把过程分享出来给大家看看,分享一下学习经验。这是第二篇,主要讲node,webpack和vue-cli环境的搭建,使用vue全家桶,完成构建静态页面,写好路由。

微信搜索 【web小馆】,回复 ”全栈博客项目“,即可获取 项目源码和后续的实战文章教程

一,前端组件的完结

1,顶部功能栏目

在这里插入图片描述

<template>
  <div class="top">
    <div class="img_div">
      <img src="../../assets/logomin.jpg" class="img">
      <div class="text">轻松学算法</div>
    </div>
    <div class="tool">
      <div class="sign" @click="login" ref="sign" v-if="!islogin">登陆</div>
      <div class="out" @click="logout" ref="out" v-if="islogin">退出</div>
      <div class="more" @click="more">更多信息</div>
    </div>
  </div>
</template>

<script>

export default {
  methods: {
    more() {
      this.$emit('more')
    },
    login() {
      this.$emit('login')
    },
    logout() {
      this.$emit('logout')
    }
  },
  computed: {
    islogin(){
      return this.$store.getters.getIsLogin
    }
  },
}
</script>

<style scoped>
.top{
  height: 50px;
  width: 100%;
  display: flex;
  background-color: #69d78a;
  position: fixed;
  top: 0;
  z-index: 1000;
}
.img_div{
  width: 180px;
  display: flex;
  margin: 5px 10px 5px 10px;
}
.img{
  height: 40px;
  width: 40px;
  border-radius: 20px;
}
.text{
  font-size: 14px;
  margin: 10px 0px 0px 20px;
  color: white;
}
.tool{
  display: flex;
  margin: 12px 10px 7px 50px;
  margin-left:auto;
}
.sign{
  margin-right: 10px;
  border: 1px white solid;
  padding: 4px 10px 5px 10px;
  border-radius: 15px;
  font-size: 14px;
  color: white;
}
.out{
  margin-right: 10px;
  border: 1px white solid;
  padding: 4px 10px 5px 10px;
  border-radius: 15px;
  font-size: 14px;
  color: white;
}
.more{
  border: 1px white solid;
  border-radius: 15px;
  padding: 4px 10px 5px 10px;
  font-size: 14px;
  color: white;
}
</style>

2,顶部导航栏

在这里插入图片描述

<template>
  <div class="top-nav">
    <div class="img_div" >
      <van-icon name="arrow-left" size="24" @click="back" color="#ffffff"/>
      <div class="text" v-html="text"></div>
    </div>
  </div>
</template>

<script>

export default {
  props: {
    text: {
      type: String,
      default: ''
    }
  },
  methods: {
    back() {
      this.$emit('back')
    },
    like() {
      this.$emit('like')
    }
  },
  computed: {
    islogin(){
      return this.$store.getters.getIsLogin
    }
  },
}
</script>

<style scoped>
.top-nav{
  height: 50px;
  width: 100%;
  display: flex;
  background-color: #69d78a;
  position: fixed;
  top: 0;
  z-index: 1000;
}
.img_div{
  width: 180px;
  display: flex;
  margin: 13px 10px 5px 10px;
}
.text{
  font-size: 14px;
  margin: 2px 0px 0px 20px;
  color: white;
}
.tool{
  display: flex;
  margin: 12px 10px 7px 50px;
  margin-left:auto;
}
</style>

3,文章详情页面detail

在这里插入图片描述

<template>
  <div class="article-new">
    <topnav @back= "back" :text= "text"></topnav>
    <scroll  :probe-type="probeType"
      :listen-scroll="listenScroll" ref="articleContentList"
      class="article-new-content"
    >
    <div>
      <div class="basicmessage" v-if= "showbasic">
        <div class="title">[两万字]面试官:听说你很懂集合源码,接我二十道问题</div>
        <div class="imformation">
          <div class="time">2020-06-28</div>
          <div class="viewnum">浏览量:{{hits}}</div>
        </div>
        <div class="author">
          <div class="imgdiv">
            <img  src="../../assets/logo.png" class="img">
          </div>
          <div class="authorimformation">
            <div class="authorname">小米粥</div>
            <div class="experience">码龄:3年</div>
          </div>
        </div>
      </div>
      <div class="sklentondiv" v-if= "!showbasic">
        <van-skeleton title :row="3" class="mysklenton"/>
        <!-- <van-skeleton title avatar class="mysklentonavatar"/> -->
      </div>

        <div class="hr"></div>
        <div v-html="compiledMarkdown" class="macked" v-if= "showmacked"></div>
        <div class="sklentondiv" v-if= "!showmacked">
          <van-skeleton title :row="3" class="mysklenton"/>
          <!-- <van-skeleton title avatar class="mysklentonavatar"/> -->
        </div>
        <div class="hrweight"></div>
        <commentarea v-if= "showcommentarea" :list= "commentcontent"></commentarea>
        <div class="sklentondiv" v-if= "!showcommentarea">
          <van-skeleton title :row="3" class="mysklenton"/>
          <!-- <van-skeleton title avatar class="mysklentonavatar"/> -->
        </div>
        <div class="hrweight"></div>
      </div>
    </scroll>

    <comment @commentsubmit= "commentsubmit" @like= "like" @good= "good" :infogood= "infogood" :infolike= "infolike"
    :goodnum= 'goods' :likenum= "likes"></comment>
  </div>
</template>

<script>
import comment from "@/base/comment/comment"
import commentarea from "@/base/commentarea/commentarea"
import topnav from '@/components/top-nav/top-nav'
import { commentItem } from "@/common/js/comment"
import { createTime } from "@/common/js/time"
import { Icon, Skeleton } from 'vant';
import marked from 'marked'
import hljs from "highlight.js";
import javascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/vs2015.css'
import { getdetail, getcomments, postcomments, addhit, addgood, addlike, sublike, subgood } from "@/api/article"
import scroll from '@/base/scroll/scroll'
import Vue from 'vue';

Vue.use(Skeleton);
export default {
  data() {
    return {
      articleContent: '',
      articleTitle: '',
      text: '最新文章',
      showbasic: false,
      showmacked: false,
      showcommentarea: false,
      commentcontent: [],
      articleid: 0,
      hits: 0,
      likes: 0,
      goods: 0,
      infogood: false,    //点赞登陆的时候就已经拿到全部的id了,还没做
      infolike: false     //收藏
    }
  },
  methods: {
    back() {
      this.$router.back()
    },
    getlogin() {
      if (!this.$store.getters.getIsLogin) {        //检验登陆  
        console.log('未登录')
        return 0
      } else {
        return 1
      }
    },
    _getdetail(id) {
      getdetail(id).then(res => {
        if (res.data.errno == 0) {
          console.log(res)
          this.articleContent = res.data.data.content
          this.showbasic = true
          this.showmacked = true
          this.showcommentarea = true
          this.articleTitle = res.data.data.title
          this.hits = res.data.data.hits
          this.likes = res.data.data.likes
          this.goods = res.data.data.goods

          addhit(id, this.hits).then(res => {
            if (res.data.errno == 0) {
              console.log(res)
            }
          })

          if (!this.getlogin()) {  //检验登陆
            return
          }     

          let mygood = this.$store.getters.getCurrentUser.goods.split(',')
          if (mygood.indexOf(this.$route.params.id)> -1) {
            this.infogood = true
          }

          let mylike = this.$store.getters.getCurrentUser.likes.split(',')
          if (mylike.indexOf(this.$route.params.id)> -1) {
            this.infolike = true
          }
        }
      })
    },

    _getcomments(id) {
      getcomments(id).then(res => {
        if (res.data.errno == 0) {
          this.articleid = res.data.data.id
          let mysqlcomment = JSON.parse(res.data.data.content)
          for (let i in mysqlcomment) {
            this.commentcontent.push(mysqlcomment[i])
          }
        }
      })
    },
    commentsubmit(content) {
      if (!this.getlogin()) {  //检验登陆
        return
      }  

      if (content == '') {
        console.log('不能评论为空')
        return
      }
      let userinfo = this.$store.getters.getCurrentUser     //登陆后用户的信息

      const nowtime = Date.now()                             //获取当前时间戳
      let item = commentItem(userinfo.avatar, userinfo.username, content, createTime(nowtime))                             //comment(avatar, username, content, time)

      this.commentcontent.push(item)                              //评论
      console.log(this.commentcontent)                                //打印已经评论的总评论

      let dataStr=JSON.stringify(this.commentcontent)                       //转化为json字符串,准备上传到数据库
      let data = new FormData()
      data.append('comments', dataStr)
      data.append('id', this.articleid)
      postcomments(data).then(res => {
        console.log(res)
      })
    },
    like() {
      if (!this.getlogin()) {  //检验登陆
        return
      } 

      if (!this.infolike) {
        let idstr = ''
        console.log(this.$store.getters.getCurrentUser.likes)
        if (this.$store.getters.getCurrentUser.likes == "") {
          idstr = this.$store.getters.getCurrentUser.likes + `${this.$route.params.id}`
        } else {
          idstr = this.$store.getters.getCurrentUser.likes + `,${this.$route.params.id}`
        }

        addlike(this.$route.params.id, idstr, this.likes).then(res => {
          console.log(res)
          let info = this.$store.getters.getCurrentUser
          info.likes = idstr
          this.$store.commit('updateUserStatus',info);

          this.likes += 1
          this.infolike = true
        })
      } else {
        let mylike = this.$store.getters.getCurrentUser.likes.split(',')
        mylike.splice(mylike.indexOf(this.$route.params.id))
        let idstr = ''
        for (let i in mylike) {
          if (mylike.length - 1> i){
            idstr += `${mylike[i]},`
          } else {
            idstr += `${mylike[i]}`
          }
        }
        sublike(this.$route.params.id, idstr, this.likes).then(res => {
          console.log(res)
          let info = this.$store.getters.getCurrentUser
          info.likes = idstr
          this.$store.commit('updateUserStatus',info);
          this.likes -= 1
          this.infolike = false
        })
      }
    },
    good() {
      if (!this.getlogin()) {  //检验登陆
        return
      } 

      if (!this.infogood) {
        let idstr = ''
        if (this.$store.getters.getCurrentUser.goods == '') {
          idstr = this.$store.getters.getCurrentUser.goods + `${this.$route.params.id}`
        } else {
          idstr = this.$store.getters.getCurrentUser.goods + `,${this.$route.params.id}`
        }
        addgood(this.$route.params.id, idstr, this.goods).then(res => {
          console.log(res)
          let info = this.$store.getters.getCurrentUser
          info.goods = idstr
          this.$store.commit('updateUserStatus',info);
          this.goods += 1
          this.infogood = true
        })
      } else {
        let mygood = this.$store.getters.getCurrentUser.goods.split(',')
        mygood.splice(mygood.indexOf(this.$route.params.id))
        let idstr = ''
        for (let i in mygood) {
          if (mygood.length - 1> i){
            idstr += `${mygood[i]},`
          } else {
            idstr += `${mygood[i]}`
          }
        }
        subgood(this.$route.params.id, idstr, this.goods).then(res => {
          console.log(res)
          let info = this.$store.getters.getCurrentUser
          info.goods = idstr
          this.$store.commit('updateUserStatus',info);
          this.goods -= 1
          this.infogood = false
        })
      }

    }
  },
  created() {
    this.listenScroll = true
    this.probeType = 3
  },
  mounted() {
  marked.setOptions({
      renderer: new marked.Renderer(),
      highlight: function (str, lang) {
  	// 此处判断是否有添加代码语言
        if (lang && hljs.getLanguage(lang)) {
          try {
            // 得到经过highlight.js之后的html代码
            const preCode = hljs.highlight(lang, str, true).value
            // 以换行进行分割
            const lines = preCode.split(/\n/)
            // 添加自定义行号
            let html = lines.map((item, index) => {
              if (index < 9) {
                return `<li> ${index + 1}| <span class="line-num" data-line="${index + 1}"></span>${item}</li>`
              } else if (9 <= index < 99) {
                return `<li>${index + 1}| <span class="line-num" data-line="${index + 1}"></span>${item}</li>`
              } else {
                return `<li>${index + 1}| <span class="line-num" data-line="${index + 1}"></span>${item}</li>`
              }

            }).join('')
            html = '<ol>' + html + '</ol>'
            // 添加代码语言
            if (lines.length) {
              html += '<b class="name">' + lang + '</b>'
            }
            return '<pre class="hljs myhljs"><code>' +
              html +
              '</code></pre>'
          } catch (__) {}
        }
      // 未添加代码语言,此处与上面同理
        const preCode = md.utils.escapeHtml(str)
        const lines = preCode.split(/\n/).slice(0, -1)
        let html = lines.map((item, index) => {
          return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>'
        }).join('')
        html = '<ol>' + html + '</ol>'
        return '<pre class="hljs"><code>' +
          html +
          '</code></pre>'
      },
      pedantic: false,
      gfm: true,
      tables: true,
      breaks: false,
      sanitize: false,
      smartLists: true,
      smartypants: false,
      xhtml: false
    }
  );

   this._getdetail(this.$route.params.id)
   this._getcomments(this.$route.params.id)
  },
  computed: {
    compiledMarkdown: function() {
      return marked(this.articleContent, {});  //第一个参数是你的markdown文本 第二个参数是选项
    },
    str: function() {
      let arr = this.$route.path.split('/')
      console.log(arr)
      let str = `当前路径为:${arr[1]}>${arr[2]}`
      return str
    },
  },
  components: {
    scroll,
    topnav,
    comment,
    commentarea
  }
}
</script>

<style scoped>
</style>

使用了mark去解析.md,并且使用highlight.js把代码部分进行高亮化处理,使得代码阅读变得更加舒服。

二,路由的编写

import Vue from 'vue'
import Router from 'vue-router'
import home from '@/components/home/home'
import article from '@/components/article/article'
import me from '@/components/me/me'
import article_group from '@/components/article_group/article_group'
import article_detail from '@/components/article_detail/article_detail'
import article_new from '@/components/article_new/article_new'
import like from '@/components/like/like'
import imformation from '@/components/imformation/imformation'
Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      name: 'home',
      component: home,
      children: [
        {
          path: ':id',
          component: article_new,
        }
      ]
    },
    {
      path: '/article',
      name: 'article',
      component: article,
      children: [
        {
          path: ':id',
          component: article_group,
          children: [
            {
              path: ':id',
              component: article_detail
            }
          ]
        }
      ]
    },
    {
      path: '/me',
      name: 'me',
      component: me,
      children: [
        {
          path: 'like',
          component: like,
          children: [
            {
              path: ':id',
              component: article_detail
            }
          ]
        },
        {
          path: 'imformation',
          component: imformation
        }
      ]
    }
  ]
})

前端主要是三个主界面,以及一些子组件的构成,运用children子路由,去写好整个应用的路由关系。并且使用路由传参,让子路由拿到信息去调用api接口。

在这里插入图片描述

你们的赞就是对我最大的鼓励。谢谢~

微信搜索【web小馆】,回复全栈博客项目,即可获取项目源码和后续的实战文章教程。每天用最简单朴实的语言,潜移默化的提升你的计算机基础知识和前端技术。小米粥,一个专注的web全栈工程师,我们下期再见!

在这里插入图片描述
node后台

猜你喜欢

转载自blog.csdn.net/gitchatxiaomi/article/details/108151290
今日推荐