头条案例day06:

搜索历史记录:


当发生搜索的时候我们才需要记录历史记录。
1 添加历史记录

//  在`search/index.vue` `data`添加一个数据用来存储历史记录
data () {
  return {中
    ...
    searchHistories: []
  }
}

//  在触发搜索的时候,记录历史记录
onSearch (val) {
  // 更新文本框内容
  this.searchText = val

  // 存储搜索历史记录
  // 要求:不要有重复历史记录、最新的排在最前面
  const index = this.searchHistories.indexOf(val)
  if (index !== -1) {
    this.searchHistories.splice(index, 1)
  }
  this.searchHistories.unshift(val)

  // 渲染搜索结果
  this.isResultShow = true
},


2 展示历史记录
需要父子通信,将数据传递给`search-history.vue`去渲染使用

//  父组件`search/index.vue`传递数据
<!-- 搜索历史记录 -->
<search-history :search-histories="searchHistories" v-else />
<!-- /搜索历史记录 -->

//  子组件`search-history.vue`接收数据
props:{
    // 搜索历史,接收外部传入数据
    searchHistories:{
        type:Array,
        required:true    
    }
}

//  渲染数据
<!-- 历史记录 -->
<van-cell-group v-else>
  <van-cell title="历史记录">
    <van-icon name="delete" />
    <span>全部删除</span>
    &nbsp;&nbsp;
    <span>完成</span>
  </van-cell>
  <van-cell
    :title="item"
    v-for="(item, index) in searchHistories"
    :key="index"
  >
    <van-icon name="close"></van-icon>
  </van-cell>
</van-cell-group>
<!-- /历史记录 -->


3 删除历史记录
基本思路:
- 给历史记录中的每一项注册点击事件
- 在处理函数中判断
  - 如果是删除状态,则执行删除操作
  - 如果是非删除状态,则执行搜索操作

4 数据持久化

//  在`search/index.vue`中利用 `watch` 监视统一存储数据
// 导入本地存储封装方法
import { setItem, getItem } from '@/utils/storage'

watch: {
  searchHistories (val) {
    // 同步到本地存储
    setItem('serach-histories', val)
  }
}

//初始化的时候从本地存储获取数据
data () {
  return {
    ...
    searchHistories: getItem('serach-histories') || [],
  }
}

文章详情:

1 创建组件并配置路由

//   创建 `views/article/index.vue` 组件
<template>
  <div class="article-container">文章详情</div>
</template>

<script>
export default {
  name: 'ArticleIndex',
  components: {},
  props: {
    // 使用props解耦获得了的动态路由数据,这样我们就可以使用this.articleId 获取动态路由数据 而不需要使用 this.$route.params.articleId  
    articleId: {
      type: [Number, String],
      required: true
    }
  },
  data () {
    return {}
  },
  computed: {},
  watch: {},
  created () {},
  mounted () {},
  methods: {}
}
</script>
<style scoped lang="less"></style>

//   然后将该页面配置到根级路由
{
  path: '/article/:articleId',
  name: 'article',
  component: () => import('@/views/article'),
  // 将路由动态参数映射到组件的 props 中,更推荐这种做法
  props: true
}

//   找到首页文章列表项组件`src/components/article-item/index.vue`配置路由跳转
<!--
    Cell 单元格的 to 属性和 VueRouter 中的 RouterLink 导航组件的 to 属性用法是一样的
    用法参考链接:https://router.vuejs.org/zh/api/#to
    :to="'/article/' + article.art_id"
    :to="`/article/${article.art_id}`"
    :to="{ name:'路径名称', params:{ 标识符:数据 } }"
-->
<van-cell
    class="article-item"
    :to="{ name: 'article', params: { articleId: article.art_id} }"
  >

2 页面布局
使用到的 Vant 中的组件:
- [NavBar 导航栏]
- [Loading 加载]
- [Cell 单元格]
- [Button 按钮]
- [Image 图片]
- [Divider 分割线]
- [Icon 图标]

3 实现功能
1、获取文章数据
思路:
- 找到数据接口
- 封装请求方法
- 请求获取数据
- 模板绑定
2、展示文章详情,渲染数据,静态展示
3、处理内容加载状态
需求:
- 加载中,显示 loading
- 加载成功,显示文章详情
- 加载失败,显示错误提示
  - 如果 404,提示资源不存在
  - 其它的,提示加载失败,用户可以点击重试重新加载

// data定义变量
loading: true, // 加载中的 loading 状态
errStatus: 0 // 失败的状态码

  async loadArticle () {
      // 展示 loading 加载中
      this.loading = true
      try {
        const { data } = await getArticleById(this.articleId)

        // if (Math.random() > 0.5) {
        //   JSON.parse('dsankljdnskaljndlkjsa')
        // }

        // 赋值
        this.article = data.data

      } catch (err) {
        if (err.response && err.response.status === 404) {
          this.errStatus = 404
        }
      }
      // 无论成功还是失败,都需要关闭 loading
      this.loading = false
    },

<!-- 加载中 -->
<div v-if="loading" class="loading-wrap"> ...  </div>
<!-- /加载中 -->

<!-- 加载完成-文章详情 -->
<div v-else-if="article.title" class="article-detail"> ... </div>
<!-- /加载完成-文章详情 -->

<!-- 加载失败:404 -->
<div v-else-if="errStatus === 404" class="error-wrap"> ... </div>
<!-- /加载失败:404 -->

<!-- 加载失败:其它未知错误(例如网络原因或服务端异常) -->
<div v-else class="error-wrap">...</div>
 <!-- /加载失败:其它未知错误(例如网络原因或服务端异常) -->

4、处理正文样式
文章正文包括各种数据:段落、标题、列表、链接、图片、视频等资源。


4-1 将 [github-markdown-css] 样式文件下载到项目中
- 把 样式文件下载放在`views/article/github-markdown.css`
- 在`article/index.vue`里面的`style`标签里面导入

<style scoped lang="less">
@import "./github-markdown.css"; 


4-2 给内容正文标签增加`markdown-body`样式类


 <!-- 文章内容 -->
 <div class="article-content markdown-body" v-html="article.content" ></div>
 <van-divider>正文结束</van-divider>


4-3 `.postcssrc.js`中配置不要转换样式文件中的字号

// 配置不要转换的样式资源
      exclude: 'github-markdown'  // 增加这一句!

需要重新启动项目,配置才会生效!

5、图片点击预览
思路:
5-1、从文章内容中获取到所有的 img DOM 节点
5-2、获取文章内容中所有的图片地址
5-3、遍历所有 img 节点,给每个节点注册点击事件
5-4、在 img 点击事件处理函数中,调用 ImagePreview 预览

<!-- 增加ref属性 -->
 <div class="article-content markdown-body"   ref="article-content" v-html="article.content" ></div>

import { ImagePreview } from 'vant'  // 导入预览插件

async loadArticle () {
     // 前面省略很多代码...
    
        // 数据驱动视图这件事儿不是立即的
        this.article = data.data

        // 初始化图片点击预览 
        console.log(this.$refs['article-content'])    // 这里没有内容 
        // 这个时候其实找不到 这个refs引用的,原因是因为v-if的渲染其实需要时间,我们视图还没有立即更新完成。
        // 使用定时器,延迟更新( setTimeout 0 会把要做的事情放在异步队列的最后面执行! )
        setTimeout(() => {
            console.log(this.$refs['article-content'])   // 这里有内容
            this.previewImage()
        }, 0)
        // 不使用定时器可以使用nextTick这个api方法
        /*
        this.loading = false
        this.$nextTick(()=>{
            this.previewImage()
        })
        */
    // 后面省略很多代码...
},

// 预览图片处理事件函数
previewImage () {
    // 得到所有的 img 节点
    const articleContent = this.$refs['article-content']  // 获取到了容器节点
    const imgs = articleContent.querySelectorAll('img')

    // 获取所有 img 地址
    const images = []
    imgs.forEach((img, index) => {
        images.push(img.src)

        // 给每个 img 注册点击事件,在处理函数中调用预览
        img.onclick = () => {
            ImagePreview({
                // 预览的图片地址数组
                images,
                // 起始位置,从 0 开始
                startPosition: index
            })
        }
    })
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值