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:
- Pass routing parameters based on question mark
- 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> <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:
- Dynamic data filling (workload)
- 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:
- Call interface to realize function
- 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> <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:
- 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> <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.
- Basic usage of local components: import first; Reconfiguration, last use
Comment list rendering
Objective: to realize the dynamic rendering of comment list data
- Get the comment list data of the article
- 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> <van-tag plain @click="showReply=true">{{comment.reply_count}} reply</van-tag> </p> </div> </div>
Summary:
- Call interface; Obtain data; Fill page
- 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:
- Call the interface to handle exceptions
- try catch handles async functions, and await is necessary