当前位置:   article > 正文

2021-09-06 黑马移动端头条项目-day07_黑马头条day08

黑马头条day08

目录

文章详情

一、创建组件并配置动态路由

二、页面布局

        2.1 标题和作者信息

        2.2 正文内容

        2.3 请求正文内容的数据

三、内容图片预览

四、关注用户

五、收藏文章 

六、文章点赞


文章详情

一、创建组件并配置动态路由

  1. // 配置动态路由,文章详情
  2. {
  3. path: '/article/:articleId',
  4. name: 'article',
  5. component: () => import('@/views/article/')
  6. }

在components/article-item.vue列表项组件中添加路由导航 to=" " 跳转至对应页面

在article/index.vue组件中获取动态路由参数:

        方式一:this.$route.params.xxx

        方式二:props 传参,推荐

采用方式二示范:router.js 给路由开启一个props: true

  1. {
  2. path: '/article/:articleId',
  3. name: 'article',
  4. component: () => import('@/views/article/'),
  5. // 动态路由传参:将动态路由参数映射到组件的props中,无论是访问还是维护都很方便
  6. props: true
  7. }

article/index.vue组件中用props申明接收:

  1. props: {
  2. articleId: {
  3. type: Number,
  4. required: true
  5. }
  6. },

二、页面布局

        2.1 标题和作者信息

        2.2 正文内容

         使用github上正文内容封装好的样式,引入 import './github-markdown.css',直接拿过来用即可 github-markdown.css

        2.3 请求正文内容的数据

        api/article.js 封装数据接口

  1. // 获取文章详情
  2. export const getArticleById = articleId => {
  3. return request({
  4. method: 'GET',
  5. url: `/app/v1_0/articles/${articleId}`
  6. })
  7. }

article/index.vue

加载接口配置: import { getArticleById } from "@/api/article";

页面初始化时加载方法,请求相应数据:

  1. created() {
  2.     this.loadArticle();
  3.   },
  4. methods: {
  5.     async loadArticle() {
  6.       const { data } = await getArticleById(this.articleId);
  7.       console.log(data);
  8.     }
  9.   }

会报错:

 解决:后端返回数据中的大数字问题

 Js能够准确表示的整数范围在-2^53到2^53之间,不包含两个端点,超过这个范围则无法精确表示该值

 后端返回的数据一般是JSON格式的字符串,若不做任何处理直接获取字符串的指定数据比较麻烦,所以要转换为js对象。

'{"id":"234894235395435","name":"jack","age": 18}' 

 转换为:

JSON.parse(  '{"id":"234894235395435","name":"jack","age": 18}'  )

可以看出超出安全整数范围的id无法精确表示这个问题并不是axios的错,解决办法是json-bigint

json-bigint是第三方包:

1.安装 npm i json-bigint

2.在utils/request.js中配置

  • 引入 import JSONbig from 'json-bigint'
  • 配置如下代码:
  1. const request = axios.create({
  2. baseURL: 'http://ttapi.research.itcast.cn/', // 接口的基准路径
  3. transformResponse: [function (data) {
  4. // 后端返回的数据可能不是json格式字符串
  5. // 如果不是,那么 jsonbig.parse调用会报错
  6. // 所以用try-catch来捕获民异常,处理异常的发生
  7. try {
  8. // 如果转换成功,则直接把结果返回
  9. return JSONbig.parse(data)
  10. } catch (err) {
  11. console.log('转换失败', err)
  12. // 如果转换失败了,则进入这里
  13. // 把数据原封不动的直接返回给请求使用
  14. return data
  15. }
  16. // axios默认在内部使用JSON.parse来转换处理原始数据
  17. // return JSON.parse(data)
  18. }]
  19. })

三、内容图片预览

点击文章里的图片可以对图片进行预览,大致步骤如下:

  • 获取文章内容DOM容器,要用ref属性

        先绑定ref属性

  1. <div
  2. class="markdown-body"
  3. v-html="article.content"
  4. ref="article-content"
  5. ></div>

        获取DOM容器

const articleContent = this.$refs['article-content']
  • 得到所有的img标签

  1. const imgs = articleContent.querySelectorAll("img");
  2. console.log(imgs);

从方面打印出来的数据看出: 数组为空,这是因为数据改变影响视图更新(DOM数据)不是立即的,如 this.article = data.data;已经改变了数据,但页面上未及时更新视图效果

 所以如果需要在修改数据之后马上操作被该数据影响的视图DOM,需要把得到所有的img标签的代码放到$nextTick中,$nextTick是Vue提供的一个方法

  1. this.$nextTick(() => {
  2. const imgs = articleContent.querySelectorAll("img");
  3. console.log(imgs);
  4. });

加了$nextTick()方法之后,效果如下

  • 循环img列表,给img注册点击事件
  • 在事件处理函数中调用ImagePreview()预览

该部分完整代码:

  1. methods: {
  2. async loadArticle() {
  3. const { data } = await getArticleById(this.articleId);
  4. console.log(data);
  5. this.article = data.data;
  6. this.$nextTick(() => {
  7. this.handlerPreviewImage();
  8. });
  9. },
  10. handlerPreviewImage() {
  11. // 进行图片预览操作
  12. // 1.获取文章内容DOM容器,要用ref属性
  13. const articleContent = this.$refs["article-content"];
  14. // 2.得到所有的img标签
  15. // 数据改变影响视图更新(DOM数据)不是立即的
  16. // 所以如果需要在修改数据之后马上操作被该数据影响的视图DOM,
  17. // 需要把得到所有的img标签的代码放到$nextTick中,
  18. // $nextTick是Vue提供的一个方法
  19. const imgs = articleContent.querySelectorAll("img");
  20. const imgPaths = []; // 收集所有图片的路径
  21. // 3.循环img列表,给img注册点击事件
  22. imgs.forEach((img, index) => {
  23. imgPaths.push(img.src);
  24. img.onclick = function() {
  25. // 4.在事件处理函数中调用ImagePreview()预览
  26. ImagePreview({
  27. images: imgPaths, // 预览图片路径列表
  28. startPosition: index // 起始位置
  29. });
  30. };
  31. });
  32. }
  33. }

四、关注用户

1.api/user.js中配置关注用户、取消关注用户的数据接口

  1. // 关注用户
  2. export const addFollow = userId => {
  3. return request({
  4. method: 'POST',
  5. url: '/app/v1_0/user/followings',
  6. data: {
  7. target: userId
  8. }
  9. })
  10. }
  11. // 取消关注用户
  12. export const deleteFollow = userId => {
  13. return request({
  14. method: 'DELETE',
  15. url: `/app/v1_0/user/followings/${userId}`
  16. })
  17. }

这两个接口都需要传用户id,即userId

 “关注”按钮绑定onFollow事件,绑定:loading事件

  1. @click="onFollow"
  2. :loading="isFollowLoading"

处理onFollow事件函数 

  1. async onFollow() {
  2. this.isFollowLoading = true;
  3. // 已关注,则取消关注
  4. if (this.article.is_followed) {
  5. await deleteFollow(this.article.aut_id);
  6. } else {
  7. // 没有关注,则添加关注
  8. await addFollow(this.article.aut_id);
  9. }
  10. this.article.is_followed = !this.article.is_followed;
  11. this.isFollowLoading = false;
  12. }

五、收藏文章 

配置数据接口

  1. // 收藏文章
  2. export const addCollect = articleId => {
  3. return request({
  4. method: 'POST',
  5. url: '/app/v1_0/article/collections',
  6. data: {
  7. target: articleId
  8. }
  9. })
  10. }
  11. // 取消收藏文章
  12. export const deleteCollect = articleId => {
  13. return request({
  14. method: 'DELETE',
  15. url: `/app/v1_0/article/collections/${articleId}`
  16. })
  17. }

收藏与取消收藏的点击事件

  1. // 收藏与取消收藏
  2. async onCollect() {
  3. // this.isCollectLoading = true;
  4. this.$toast.loading({
  5. message: "操作中...",
  6. forbidClick: true // 禁止背景点击
  7. });
  8. // 已收藏,则取消收藏
  9. if (this.article.is_collected) {
  10. await deleteCollect(this.articleId);
  11. } else {
  12. // 没有收藏,则添加收藏
  13. await addCollect(this.articleId);
  14. }
  15. this.article.is_collected = !this.article.is_collected;
  16. // this.isCollectLoading = false;
  17. this.$toast.success(`${this.article.is_collected ? "" : "取消"}收藏成功`);
  18. }

六、文章点赞

配置数据接口

  1. // 点赞文章
  2. export const addLike = articleId => {
  3. return request({
  4. method: 'POST',
  5. url: '/app/v1_0/article/likings',
  6. data: {
  7. target: articleId
  8. }
  9. })
  10. }
  11. // 取消点赞文章
  12. export const deleteLike = articleId => {
  13. return request({
  14. method: 'DELETE',
  15. url: `/app/v1_0/article/likings/${articleId}`
  16. })
  17. }

点赞与取消点赞的点击事件

  1. // 对文章点赞或取消点赞
  2. async onLike() {
  3. this.$toast.loading({
  4. message: "操作中...",
  5. forbidClick: true // 禁止背景点击
  6. });
  7. if (this.article.attitude === 1) {
  8. // 取消点赞
  9. await deleteLike(this.article.art_id);
  10. this.article.attitude = -1;
  11. } else {
  12. // 添加点赞
  13. await addLike(this.article.art_id);
  14. this.article.attitude = 1;
  15. }
  16. this.$toast.success(
  17. `${this.article.attitude === 1 ? "" : "取消"}点赞成功`
  18. );
  19. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/988094
推荐阅读
相关标签
  

闽ICP备14008679号