Mobile Project article management

Posted by j007ha on Wed, 02 Feb 2022 00:26:05 +0100

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