Dark horse headline project - Vue-day9 - Article Details module, follow and cancel follow, praise and like functions

Posted by pauleth on Sun, 30 Jan 2022 21:53:38 +0100

Article details module

Configure article detail routing

Objective: configure article detail component routing

  • Routing configuration
import Detail from '@/views/detail/index.vue'
{ path: '/detail', component: Detail }
  • Control route jump
<!-- van-cell The component supports route jump, similar to router-link Tagged to attribute -->
<van-cell :to='{path: "/detail", query: {id: item.art_id.toString()}}' v-for='item in list' :key='item.art_id.toString()'>
<!-- Based on traditional programmed navigation, you can also jump -->
<van-cell @click='$router.push("/detail?id="+item.art_id.toString())' v-for='item in list' :key='item.art_id.toString()'>

Summary:

  1. Pass routing parameters based on question mark
  2. The vant cell component supports route jump

Get article details data

Objective: to obtain article details data

  • Encapsulate interface call method
// Get the detailed data of the article
export const getDetailById = (articleId) => {
  return request({
    method: 'get',
    url: 'v1_0/articles/' + articleId
  })
}

  • Call the interface to get the article details data
  data () {
    return {
      article: {}
    }
  },
  methods: {
    async getDetailById () {
      const ret = await getDetailById(this.$route.query.id)
      this.article = ret.data
    }
  },
  created () {
    this.getDetailById()
  }
  • Render article content
<div class='container' ref="container">
  <van-nav-bar fixed title="Article details" left-arrow @click-left="$router.back()" />
  <div class="detail" v-if="article">
    <h3 class="title">{{article.title}}</h3>
    <div class="author">
      <van-image round width="1rem" height="1rem" fit="fill" :src="article.aut_photo" />
      <div class="text">
        <p class="name">{{article.aut_name}}</p>
        <p class="time">{{article.pubdate|relativeTime}}</p>
      </div>
      <van-button round size="small" type="info">
         {{article.is_followed?'Paid attention to':'+ follow'}}
      </van-button>
    </div>
    <div class="content" v-html="article.content"></div>
    <div class="zan">
      <van-button round size="small" :class="{active:article.attitude===1}" plain icon="like-o">give the thumbs-up</van-button>
      &nbsp;&nbsp;&nbsp;&nbsp;
      <van-button round size="small" :class="{active:article.attitude===0}" plain icon="delete">dislike</van-button>
    </div>
  </div>
</div>
  • Detail style
.container {
  height: 100%;
  overflow-y: auto;
  box-sizing: border-box;
}
.detail {
  padding: 92px 20px 88px;
  // height: 1000%;
  .title {
    font-size: 36px;
    line-height: 2;
  }
  .zan {
    text-align: center;
    padding: 20px 0;
    .active {
      border-color: red;
      color: red;
    }
  }
  .author {
    padding: 20px 0;
    display: flex;
    .text {
      flex: 1;
      padding-left: 20px;
      line-height: 1.5;
      .name {
        font-size: 28px;
        margin: 0;
      }
      .time {
        margin: 0;
        font-size: 24px;
        color: #999;
      }
    }
  }
  .content {
    padding: 40px 0;
    overflow: hidden;
    white-space: pre-wrap;
    word-break: break-all;
    /deep/ img {
      max-width: 100%;
      background: #f9f9f9;
    }
    /deep/ code {
      white-space: pre-wrap;
    }
    /deep/ pre {
      white-space: pre-wrap;
    }
  }
}

Summary:

  1. Dynamic data filling (workload)
  2. Follow function / like function

Article attention and cancellation

Objective: to realize and cancel the following articles

  • Binding event
<van-button @click="followed()" round size="small" type="info">
  {{article.is_followed?'Paid attention to':'+ follow'}}
</van-button>
  • Encapsulate concern and cancel concern interface
// Focus on article interface
export const followArticle = (userId) => {
  return request({
    method: 'post',
    url: 'v1_0/user/followings',
    data: {
      target: userId
    }
  })
}

// Focus on article interface
export const unFollowArticle = (userId) => {
  return request({
    method: 'delete',
    url: 'v1_0/user/followings/' + userId
  })
}
  • Realize function
import { followArticle, unFollowArticle } from '@/api/api-article.js'
// Follow or cancel the article
async toggleFocus () {
  // Judge the operation of paying attention and canceling attention
  if (this.article.is_followed) {
    // You have already paid attention. You need to cancel the attention at this time
    try {
      await unFollowArticle(this.article.aut_id)
      this.article.is_followed = !this.article.is_followed
    } catch {
      this.$toast('Failed to cancel attention')
    }
  } else {
    // No attention has been paid. Attention is needed at this time
    try {
      await followArticle(this.article.aut_id)
      this.article.is_followed = !this.article.is_followed
    } catch {
      this.$toast('Focus on failure')
    }
  }
},

Summary:

  1. Call interface to realize function
  2. The exception handling of async function must have await, otherwise catch cannot be executed, because Promise will use the catch method to handle exceptions.

Likes and dislikes of articles

Objective: to realize the function of likes and dislikes of articles

  • Binding event
<div class="zan">
  <van-button @click="praise(1)" round size="small" :class="{active:article.attitude===1}" plain icon="like-o">give the thumbs-up</van-button>
  &nbsp;&nbsp;&nbsp;&nbsp;
  <van-button @click="praise(0)" round size="small" :class="{active:article.attitude===0}" plain icon="delete">dislike</van-button>
</div>
  • Encapsulate like and dislike interfaces
// Like interface
export const likes = (articleId) => {
  return request({
    method: 'post',
    url: 'v1_0/article/likings',
    data: {
      target: articleId
    }
  })
}

// Cancel liking interface
export const unlikes = (articleId) => {
  return request({
    method: 'delete',
    url: 'v1_0/article/likings/' + articleId
  })
}

// Add [dislike] interface
export const dislikes = (articleId) => {
  return request({
    method: 'post',
    url: 'v1_0/article/dislikes',
    data: {
      target: articleId
    }
  })
}

// Cancel [dislike] interface
export const undislikes = (articleId) => {
  return request({
    method: 'delete',
    url: 'v1_0/article/dislikes/' + articleId
  })
}
  • Realize function
// Achieve the praise effect (4 operations: praise and cancel, dislike and cancel dislike)
async toggleStatus (type) {
  // Which button did you click?
  if (type === 1) {
    // Click the first button: like / cancel like
    if (this.article.attitude === 1) {
      // Cancel like
      try {
        await unlikes(this.article.art_id.toString())
        this.article.attitude = -1
      } catch {
        this.$toast('Failed to cancel liking')
      }
    } else {
      // give the thumbs-up
      try {
        await likes(this.article.art_id.toString())
        this.article.attitude = 1
      } catch {
        this.$toast('Failed to like')
      }
    }
  } else {
    // Click the second button: dislike / cancel dislike
    if (this.article.attitude === 0) {
      // Cancel dislike
      try {
        await undislikes(this.article.art_id.toString())
        this.article.attitude = -1
      } catch {
        this.$toast('Cancel dislike failure')
      }
    } else {
      // dislike
      try {
        await dislikes(this.article.art_id.toString())
        this.article.attitude = 0
      } catch {
        this.$toast('Don't like failure')
      }
    }
  }
},

Summary:

  1. There are many logical branches, first macro and then micro (deal with large branches first and then details)

Article comment module

Comment component split

Objective: split the article comment component and realize its function

  • Basic layout of components
<div class="comment">
  <!-- Comment list -->
  <van-list v-model="loading" :finished="finished" finished-text="No more">
    <div class="item van-hairline--bottom van-hairline--top" v-for="index in 5"               :key="index">
      <van-image round width="1rem" height="1rem" fit="fill"                                   src="https://img.yzcdn.cn/vant/cat.jpeg" />
      <div class="info">
        <p>
          <span class="name">A breeze</span>
          <span style="float:right">
            <span class="van-icon van-icon-good-job-o zan"></span>
            <span class="count">10</span>
          </span>
        </p>
        <p>The content of the comment,....</p>
        <p>
          <span class="time">Within two days</span>&nbsp;
          <van-tag plain @click="showReply=true">4 reply</van-tag>
        </p>
      </div>
    </div>
  </van-list>
  <!-- Bottom input box -->
  <div class="reply-container van-hairline--top">
    <van-field v-model="value" placeholder="Write comments...">
      <span class="submit" slot="button">Submit</span>
    </van-field>
  </div>
</div>
  • Layout style
.comment {
  margin-top: 20px;
  /deep/ .item {
    display: flex;
    padding: 20px 0;
    .info {
      flex: 1;
      padding-left: 20px;
      .name {
        color: #069;
      }
      .zan {
        vertical-align: middle;
        padding-right: 4px;
      }
      .count {
        vertical-align: middle;
        font-size: 20px;
        color: #666;
      }
      .time {
        color: #666;
      }
      p {
        padding: 10px 0;
        margin: 0;
      }
    }
  }
  /deep/ .van-button:active::before {
    background: transparent;
  }
}
.reply-container {
  position: fixed;
  left: 0;
  bottom: 0;
  height: 88px;
  width: 100%;
  background: #f5f5f5;
  z-index: 9999;
  .submit {
    font-size: 24px;
    color: #3296fa;
  }
}
  • Comment on relevant data
data () {
  return {
    value: '',
    loading: false,
    finished: false
  }
}

Summary: basic layout of comment list and comment form.

  1. Basic usage of local components: import first; Reconfiguration, last use

Comment list rendering

Objective: to realize the dynamic rendering of comment list data

  1. Get the comment list data of the article
  2. Get the list data of replies to comments
  • Encapsulated list data interface
// Get comments on articles
export const getComments = (articleId, offset) => {
  return request({
    method: 'get',
    url: 'v1_0/comments',
    params: {
      // a refers to the comment of the article; c represents the data in response to comments
      type: 'a',
      // The id of the comment article or the id of the reply comment
      source: articleId,
      // Paging parameter (id of comment)
      offset: offset,
      // Number of entries per page
      limit: 10
    }
  })
}

Summary: be familiar with the paging strategy based on offset: sort and page according to the id of article comments, which is similar to the paging method based on timestamp.

  • Dynamically load comment list data
async onLoad () {
  // Call the interface to get comment data
  const ret = await getComments(this.articleId, this.offset)
  this.list.push(...ret.data.results)
  // Modify the status bit of this load
  this.loading = false
  // Conditions for judging the end
  if (ret.data.last_id === ret.data.end_id) {
    // The list data has been loaded completely
    this.finished = true
  } else {
    // Record the starting position of the next page of data query
    this.offset = ret.data.last_id
  }
}
  • Dynamic rendering of list data
<div class="item van-hairline--bottom van-hairline--top" v-for="comment in list" :key="comment.com_id.toString()">
  <van-image round width="1rem" height="1rem" fit="fill" :src="comment.aut_photo" />
  <div class="info">
    <p>
      <span class="name">{{comment.aut_name}}</span>
      <span style="float:right">
        <span class="van-icon van-icon-good-job-o zan"></span>
        <span class="count">{{comment.like_count}}</span>
      </span>
    </p>
    <p>{{comment.content}}</p>
    <p>
      <span class="time">{{comment.pubdate|formatTime}}</span>&nbsp;
      <van-tag plain @click="showReply=true">{{comment.reply_count}} reply</van-tag>
    </p>
  </div>
</div>

Summary:

  1. Call interface; Obtain data; Fill page
  2. The condition (understanding) for judging the end of paging is similar to the paging strategy based on timestamp

Article comment function

Objective: to realize the comment function

  • Encapsulation interface
// Comment interface
export const comment = (options) => {
  return request({
    method: 'post',
    url: 'v1_0/comments',
    data: {
      // id of the article
      target: options.target,
      // Comment content
      content: options.content,
      // If the parameter target is the article id, this parameter is not required
      // If the parameter target is the comment id, this parameter represents the article id
      art_id: options.articleId
    }
  })
}
  • Binding event
<span class="submit" @click="handleSubmit()" slot="button">Submit</span>
  • Realize the comment reply function
async sendComment () {
  // Comment merit
  try {
    await comment({
      target: this.articleId,
      content: this.value
    })
    this.list = []
    this.offset = null
    this.onLoad()
    this.value = ''
  } catch {
    this.$toast('Comment article failed')
  }
},

Summary:

  1. Call the interface to handle exceptions
  2. try catch handles async functions, and await is necessary

Topics: ECMAScript Vue