1, Create component configuration route
1. Create views / article / index Vue component
2. Configure this page to the root level route
{ path: '/article/:articleId', name: 'article', component: () => import('@/views/article') }
3. In SRC \ components \ article item \ index Binding in the article list in Vue
<van-cell class="article-item" :to="`/article/${article.art_id}`"> </van-cell>
4. Use component Props to decouple routing parameters
Because the address bar of each item in the article list is with articleId
I want to decouple the routing parameters, that is, put the parameters into the props of the component.
If it is reused elsewhere, this parameter can be passed from parent to child.
Step 1 Now the current component declares a props to receive the parameter articleId
export default { name: 'ArticleIndex', props: { articleId: { type: [Number, String], //Multiple types may be strings or numbers required: true } } }
Step 2 The route passes props parameters in the router JS to change the route and enable props to pass parameters. Therefore, you can get the parameter articleId just passed in the sub component
{ path: '/article/:articleId', name: 'article', component: () => import('@/views/article'), // To enable props parameter transmission is to map the routing parameters to the props of the component props: true }
2, The page layout part is omitted
The vant components used are:
3, Show article details
Main contents:
- Data interface found
- Encapsulation request method
- Request data
- Template binding
1. Add interface method in API / article JS
//Get article export const getArticleById = articleId =>{ return request({ method:'GET', url:`/v1_0/articles/${articleId}` }) }
2. Import {getarticlebyid} from '@ / API / article JS' get article details
export default { name: 'ArticleIndex', props: { articleId: { type: [Number, String], required: true } }, data () { return { article: {} // Article details } }, created () { this.loadArticle() }, methods: { async loadArticle () { try { const { data } = await getArticleById(this.articleId) console.log(data) this.article = data.data } catch (error) { console.log('Failed to get data', error) } } } }
3. Use the processed data and use the template for binding
4, Processing content loading status
Demand analysis:
- Displaying loading when loading
- Loading succeeded, show article details
- Load error display error prompt
- 404 prompt resource does not exist
- If other prompts fail to load, users can click reload
1. Declare loading in data, i.e. errStatus status
data () { return { article: {}, // Article details loading: true, // Loading in loading errStatus: 0 // Failed status code } }
2. Put it into methods
methods:{ async loadArticle(){ this.loading = true //Show loading first try{ const {data} = await getArticleById(this.articleId) this.article = data.data }catch(error){ if (error.response && error.response.status === 404) { this.errStatus = 404 } } //Turn off loading for success or failure this.loading = false } }
3. Optimize on the page
<!-- Load complete-Article details --> <div class="article-detail" v-else-if="article.title"></div> <!-- Load failed: 404 --> <div class="error-wrap" v-else-if="errStatus === 404"></div> <!-- Loading failure: other unknown errors (such as network reasons or server exceptions) --> <div v-else class="error-wrap"></div> </div>
5, The text style directly uses the downloaded resources
@import './github-markdown.css';
module.exports = { plugins: { 'postcss-pxtorem': { rootValue ({ file }) { return file.indexOf('vant') !== -1 ? 37.5 : 75 }, // rootValue: 75, // Configure CSS properties to convert // *Indicates all propList: ['*'], exclude: 'github-markdown' } } }
Example usage:
<div class="contain markdown-bode"></div>
6, Click preview picture
Idea:
- Get all img DOM nodes from the article content
- Get the address of all pictures in the article content
- Traverse all img nodes and add click events to each node
- Call ImagePreview preview in click event handler
1. Use of imagepreview component
import { ImagePreview } from 'vant' ImagePreview(['xxx'])
2. Method of encapsulating ImagePreview preview image
previewImage(){ //Get all img nodes const articleContent = this.$ref['article-content'] const imgs = articleContent.querySelectorAll('img') const images = [] imgs.forEach((item,index) => { //Get the address of all pictures in the article content images.push(item.src) item.onclick = () =>{ ImagePreview([ images:images, //Assign an array to it startPosition: index ]) } }) }
3. Finally, add a timer in loadArticle() to let the image preview operation be executed at the end
setTimeout(() => { this.previewImage() }, 0);
7, Focus on users
Idea:
- Register click event for button
- Create event handler function
- Find the data interface encapsulation request method and request to call the view update
1. Encapsulation request method
//Add attention export const addFollow = target => { return request({ method: 'POST', url: '/v1_0/user/followings', data: { target } }) } // Cancel attention export const deleteFollow = target => { return request({ method: 'DELETE', url: `/v1_0/user/followings/${target}` }) }
2. Bind click event to button
<van-button v-if="article.is_followed" class="follow-btn" type="info" color="#3296fa" round size="small" icon="plus" @click="onFollow"> follow </van-button> <van-button v-else class="articlefollow-btn" round size="small" @click="onFollow"> Paid attention to </van-button> methods:{ onFollow(){} }
3. Event handling function
import { addFollow, deleteFollow } from '@/api/user'
async onFollow(){ try{ if(this.article.is_followed){ //Paid attention, cancelled await deleteFollow(this.article.aut_id) }else{ await addFollow(this.article.aut_id) } this.article.is_followed = !this.article.is_followed }catch(error){ console.log(error) let message = 'Operation failed, please try again' if (error.response && error.response.status === 400) { message = 'You can't focus on yourself!' } this.$toast(message) } }
4. Add loading effect
<van-button :loading="followLoading" v-if="is_followed" class="follow-btn" type="info" color="#3296fa" round size="small" icon="plus" @click="onFollow"> follow </van-button> <van-button :loading="followLoading" v-else class="follow-btn" round size="small" @click="onFollow"> Paid attention to </van-button> data() { return { // ..... followLoading: false // Whether the loading status is displayed } }, methods:{ // Follow and unsubscribe async onFollow() { this.followLoading = true try { // ... } catch (error) { // ... } this.followLoading = false } }
4. In components \ follow user \ index Declare encapsulated components in Vue
Import into parent component:
import FollowUser from '@/components/follow-user/index.vue' //Will be_ Followed incoming subcomponent <follow-user class="follow-btn" @update-is_followed="article.is_followed = $event" :is-followed="article.is_followed" :user-id="article.aut_id" />
Sub components:
<template> <van-button v-if="isFollowed" @click="onFollow" class="follow-btn" type="info" color="#3296fa" round size="small" icon="plus" :loading="loading" >follow</van-button> <van-button :loading="loading" @click="onFollow" v-else class="follow-btn" round size="small">Paid attention to</van-button> </template> <script> import { addFollow, deleteFollow } from '@/api/user.js' export default { name: 'FollowUser', props: { isFollowed: { type: Boolean, required: true } }, data() { return { loading: false // Whether the loading status is displayed } }, methods: { // Follow and unsubscribe async onFollow() { this.followLoading = true try { if (this.isFollowed) { // Paid attention, cancelled await deleteFollow(this.userId) } else { await addFollow(this.userId) } // this.article.is_followed = !this.article.is_followed this.$emit('update-is_followed', !this.isFollowed) } catch (error) { let message = 'Operation failed, please try again' if (error.response && error.response.status === 400) { message = 'You can't focus on yourself!' } this.$toast(message) } this.followLoading = false } } </script> <style lang="less" scoped> </style>
8, Article collection
The idea is the same as that of paying attention to users
1. Package components \ collect article \ index vue
Import the child component into the parent component and mount it in the page for use
2. The processing view passes the id and whether it is selected to the sub component
<collect-article v-model="article.is_collected" :article-id="article.art_id" />
Subcomponent props receive
props: { value: { type: Boolean, required: true }, articleId: { type: [Number, String, Object], required: true } }
Render the page with the passed data
<template> <van-icon :color="value ? '#ffa500' : ''" :name="value ? 'star' : 'star-o'" /> </template>
3. Function processing
The ideas and concerns are the same:
- Register click events for the favorites button
- If you have already collected, cancel the collection
- If there are no collections, add a collection
(1). In API / article JS adds two data interfaces: encapsulating collected articles and canceling collected articles
(2). Register click event @ Click = "onCollect" for favorite button
(3). Business logic for processing collections
methods: { async onCollect() { this.$toast.loading({ duration: 0, // Keep showing toast message: 'In operation...', forbidClick: true // Prohibit background clicking }) try { if (this.value) { // Collected await deleteCollect(this.articleId) } else { // No collection await addCollect(this.articleId) } // update the view this.$emit('input', !this.value) // Tips after successful collection this.$toast.success(!this.value ? 'Collection success' : 'Cancel collection') } catch (error) { this.$toast.fail('Operation failed, please try again!') } } }
9, Article praise
The idea is the same as collection and attention