一、创建组件配置路由
1.创建 views/article/index.vue
组件
2.将该页面配置到根级路由
{
path: '/article/:articleId',
name: 'article',
component: () => import('@/views/article')
}
3.在 src\components\article-item\index.vue
中的文章列表中绑定
<van-cell class="article-item" :to="`/article/${article.art_id}`">
</van-cell>
4.使用组件 Props
解耦合路由参数
由于文章列表的每一项的地址栏里是带articleId的
希望对路由参数进行解耦处理 就是说把参数放到组件的props中。
假如说在别的地方被重用,就可以通过父传子的方式来传这个参数。
步骤1.现在当前组件声明一个props来接收参数articleId
export default {
name: 'ArticleIndex',
props: {
articleId: {
type: [Number, String], //多类型 可能是字符串 可能是数字
required: true
}
}
}
步骤2.路由传递props参数 在router.js中更改路由 开启props传参 。因此在子组件中就可以拿到刚刚传递的参数articleId
{
path: '/article/:articleId',
name: 'article',
component: () => import('@/views/article'),
// 开启 props 传参,说白了就是把路由参数映射到组件的 props 中
props: true
}
二、页面布局部分 省略
使用到的vant组件有:
三、展示文章详情
主要内容:
- 找到数据接口
- 封装请求方法
- 请求获取数据
- 模板绑定
1.新增接口方法,在api/article.js中
//获取文章
export const getArticleById = articleId =>{
return request({
method:'GET',
url:`/v1_0/articles/${articleId}`
})
}
2.导入import { getArticleById } from ‘@/api/article.js’ 获取文章详情
export default {
name: 'ArticleIndex',
props: {
articleId: {
type: [Number, String],
required: true
}
},
data () {
return {
article: {} // 文章详情
}
},
created () {
this.loadArticle()
},
methods: {
async loadArticle () {
try {
const { data } = await getArticleById(this.articleId)
console.log(data)
this.article = data.data
} catch (error) {
console.log('获取数据失败', error)
}
}
}
}
3.使用处理好以后的数据,使用模板进行绑定 略
四、处理内容加载状态
需求分析:
- 在加载中时显示loading
- 加载成功显示文章详情
- 加载错误显示错误提示
- 404 提示资源不存在
- 其他的提示 加载失败 用户可以点击重新加载
1.在data中声明loading即errStatus状态
data () {
return {
article: {}, // 文章详情
loading: true, // 加载中的 loading
errStatus: 0 // 失败的状态码
}
}
2.放到methods中
methods:{
async loadArticle(){
this.loading = true //进来先显示加载
try{
const {data} = await getArticleById(this.articleId)
this.article = data.data
}catch(error){
if (error.response && error.response.status === 404) {
this.errStatus = 404
}
}
//无论是成功还是失败 都要关闭loading
this.loading = false
}
}
3.在页面上进行优化
<!-- 加载完成-文章详情 -->
<div class="article-detail" v-else-if="article.title"></div>
<!-- 加载失败:404 -->
<div class="error-wrap" v-else-if="errStatus === 404"></div>
<!-- 加载失败:其它未知错误(例如网络原因或服务端异常) -->
<div v-else class="error-wrap"></div>
</div>
五、正文样式 直接用下载的资源
@import './github-markdown.css';
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue ({ file }) {
return file.indexOf('vant') !== -1 ? 37.5 : 75
},
// rootValue: 75,
// 配置要转换的 CSS 属性
// * 表示所有
propList: ['*'],
exclude: 'github-markdown'
}
}
}
示例用法:
<div class="contain markdown-bode"></div>
六、图片点击预览
思路:
- 从文章内容中获取到所有的 img DOM 节点
- 获取文章内容中所有的图片地址
- 遍历所有的img节点 给每个节点添加点击事件
- 在点击事件处理函数中 调用
ImagePreview
预览
1.ImagePreview
组件的使用
import { ImagePreview } from 'vant'
ImagePreview(['xxx'])
2.封装 ImagePreview
预览图片方法
previewImage(){
//获取所有的img节点
const articleContent = this.$ref['article-content']
const imgs = articleContent.querySelectorAll('img')
const images = []
imgs.forEach((item,index) => {
//获取文章内容中所有的图片地址
images.push(item.src)
item.onclick = () =>{
ImagePreview([
images:images, //将数组赋给它
startPosition: index
])
}
})
}
3.最后在loadArticle()中添加一个定时器 让图片预览的操作放在最后执行
setTimeout(() => {
this.previewImage()
}, 0);
七、关注用户
思路:
- 给按钮注册点击事件
- 创建事件处理函数
- 找到数据接口 封装请求方法 请求调用 视图更新
1.封装请求方法
//添加关注
export const addFollow = target => {
return request({
method: 'POST',
url: '/v1_0/user/followings',
data: {
target
}
})
}
// 取消关注
export const deleteFollow = target => {
return request({
method: 'DELETE',
url: `/v1_0/user/followings/${target}`
})
}
2.给按钮绑定点击事件
<van-button
v-if="article.is_followed"
class="follow-btn"
type="info"
color="#3296fa"
round size="small"
icon="plus"
@click="onFollow">
关注
</van-button>
<van-button
v-else
class="articlefollow-btn"
round
size="small"
@click="onFollow">
已关注
</van-button>
methods:{
onFollow(){}
}
3.事件处理函数
import { addFollow, deleteFollow } from '@/api/user'
async onFollow(){
try{
if(this.article.is_followed){
//已关注,取消关注
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 = '操作失败,请重试'
if (error.response && error.response.status === 400) {
message = '你不能关注你自己!'
}
this.$toast(message)
}
}
4.添加loading效果
<van-button :loading="followLoading" v-if="is_followed" class="follow-btn" type="info" color="#3296fa" round size="small" icon="plus" @click="onFollow">
关注
</van-button>
<van-button :loading="followLoading" v-else class="follow-btn" round size="small" @click="onFollow">
已关注
</van-button>
data() {
return {
// .....
followLoading: false // loading 状态显示与否
}
},
methods:{
// 关注和取消关注
async onFollow() {
this.followLoading = true
try {
// ...
} catch (error) {
// ...
}
this.followLoading = false
}
}
4.在 components\follow-user\index.vue
中声明封装以后的组件
导入父组件中:
import FollowUser from '@/components/follow-user/index.vue'
//将is_followed传入子组件
<follow-user
class="follow-btn"
@update-is_followed="article.is_followed = $event"
:is-followed="article.is_followed"
:user-id="article.aut_id"
/>
子组件:
<template>
<van-button
v-if="isFollowed"
@click="onFollow"
class="follow-btn"
type="info"
color="#3296fa"
round
size="small"
icon="plus"
:loading="loading"
>关注</van-button>
<van-button :loading="loading" @click="onFollow" v-else class="follow-btn" round size="small">已关注</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 // loading 状态显示与否
}
},
methods: {
// 关注和取消关注
async onFollow() {
this.followLoading = true
try {
if (this.isFollowed) {
// 已关注,取消关注
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 = '操作失败,请重试'
if (error.response && error.response.status === 400) {
message = '你不能关注你自己!'
}
this.$toast(message)
}
this.followLoading = false
}
}
</script>
<style lang="less" scoped>
</style>
八、文章收藏
思路同关注用户部分
1.封装组件components\collect-article\index.vue
将子组件导入父组件并挂载 在页面中使用
2.处理视图 将id和是否选中传给子组件
<collect-article v-model="article.is_collected" :article-id="article.art_id" />
子组件props接收
props: {
value: {
type: Boolean,
required: true
},
articleId: {
type: [Number, String, Object],
required: true
}
}
使用传递的数据渲染页面
<template>
<van-icon :color="value ? '#ffa500' : ''" :name="value ? 'star' : 'star-o'" />
</template>
3.功能处理
思路 和关注相同:
- 给收藏按钮注册点击事件
- 如果已经收藏了,则取消收藏
- 如果没有收藏,则添加收藏
(1).在 api/article.js
添加封装收藏文章和取消收藏文章两个数据接口
(2).给收藏按钮注册点击事件 @click=“onCollect”
(3).处理收藏的业务逻辑
methods: {
async onCollect() {
this.$toast.loading({
duration: 0, // 持续展示 toast
message: '操作中...',
forbidClick: true // 是否禁止背景点击
})
try {
if (this.value) {
// 已收藏
await deleteCollect(this.articleId)
} else {
// 没有收藏
await addCollect(this.articleId)
}
// 更新视图
this.$emit('input', !this.value)
// 收藏成功以后的提示
this.$toast.success(!this.value ? '收藏成功' : '取消收藏')
} catch (error) {
this.$toast.fail('操作失败,请重试!')
}
}
}
九、文章点赞
思路和收藏和关注相同