【珍惜时间】vue.news

哈哈好久不见,我们一起来欣赏代码呀
老规矩,放一下博主大大的github地址:https://github.com/daoket/vue.news
接下来我们来看看效果呀

接下来我们来一起看看代码呀
其实看了动图,我有几个想知道的实现的点,1.点击加载更多,2.中间部分的切换,如果有很多的UI,也可以实现很美好的效果呢3.然后就是侧边栏的
接下来我们一起看代码呀
我们在main.js中可以看到,项目是有使用懒加载的

//main.js
import Vue from 'vue'
import App from './App'
import router from './router'

import './style/public.css'

import axios from 'axios' // 处理http请求
import store from './store' // 状态管理
import VueLazyload from 'vue-lazyload' // 懒加载

/**
 * @desc 懒加载配置
 * @author wtniu
 */
Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: './assets/error.jpg',
  loading: './assets/loading.gif',
  attempt: 1
})

Vue.prototype.$axios = axios

// 去掉开发环境打印信息
Vue.config.productionTip = false
Vue.config.devtools = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

然后我们看看app.vue吧
app.vue中是比较有意思的,引用了四个组件,头部,底部中间部分以及侧边的

//app.vue
<template>
  <div id="app">
    <app-menu></app-menu>
    <div class="page">
      <app-head></app-head>
      <app-nav></app-nav>
      <!--keep-alive 切换路不会触发mounted-->
      <keep-alive><router-view></router-view></keep-alive>
      <app-foot></app-foot>
    </div>
  </div>
</template>

<script>
import AppHead from './components/public/Head'
import AppMenu from './components/public/Menu'
import AppNav from './components/public/Nav'
import AppFoot from './components/public/Foot'

export default {
  name: 'app',
  components: {
    'app-head': AppHead,
    'app-menu': AppMenu,
    'app-nav': AppNav,
    'app-foot': AppFoot
  }
}
</script>

<style>
#app{
  overflow: hidden;
}
.page{
  position: relative;
  z-index: 99;
  transition: all 0.5s;
}
.toggle{
  transform: translateX(-120px);
}
</style>

我们先看头部和底部的,感觉很好写的
先看footer啦

//foot.vue
<template>
  <footer class="foot">
    <img src="../../assets/head/logo.png"/>
    <div class="email">
      <p>联系我:wt_niu@163.com</p>
      <a href="https://github.com/daoket/vue.news"><p>Fork me on GitHub</p></a>
    </div>
  </footer>
</template>

<style lang="scss">
.foot{
  height: 100px;
  background-color: #262627;
  display: flex;
  color: #fff;
  justify-content: space-around;
  align-items: center;
  img{
    height: 20px;
    margin-left: 10%;
    padding-right: 5%;
    border-right: 1px solid #666;
  }
  .email{
    font-size: 13px;
    p{
      margin: 10px 0;
    }
  }
}
</style>

其实head部分我认为内容很少的,怎么感觉代码超出了自己的认知

作者大大写的时候,把右边menu,点击关闭和展示,是写在了store里面,然后还有回到首页的功能,不过我好像没有看到搜索的功能
后面看代码的时候发现是作者大大自己注释了

//head.vue
<template>
  <header class="head">
    <a href="javascript: void(0)"><img class="vNews" @click="goHome" src="../../assets/head/logo.png"/></a>
    <svg class="icon searchBtn" @click='openSearch' aria-hidden="true">
      <use xlink:href="#icon-sousuo"></use>
    </svg>
    <div class="searchPage">
      <div class="header">
        <div class="search">
          <input v-model='searchContent' type="text" />
          <svg class="icon" @click='searchNewsBtn' aria-hidden="true">
            <use xlink:href="#icon-sousuo"></use>
          </svg>
        </div>
        <svg class="icon close" @click='closeSearch' aria-hidden="true">
          <use xlink:href="#icon-hao"></use>
        </svg>
      </div>
      <div class="content">
        <p class="today">今天</p>
        <ul class="news">
          <li ref='newsItem' v-for='(news, index) in searchNews' :key='index'>
            <a :href="'#' + news.id" @click='goNews'>
              <p v-if='+index < 3'><i class="isTop3"> {{index + 1}} </i><span ref='title' class="title"> {{news.title}}</span></p>
              <p v-else><i> {{index + 1}} </i><span ref='title' class="title"> {{news.title}}</span></p>
            </a>
          </li>
        </ul>
      </div>
    </div>
    <div class="aside" @click='toggleMenu'>
      <div v-for='i in 3' :key='i' class="line"></div>
    </div>
  </header>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'apphead', // Do not use built-in or reserved HTML elements as component id
  data () {
    return {
      imgs: [],
      searchContent: ''
    }
  },
  computed: {
    ...mapState({
      searchNews: state => state.SelectStore.searchNews
    })
  },
  watch: {
    searchContent (curVal) {
      if (curVal === '') {
        this.$refs.newsItem.map((item) => {
          item.style.display = 'block'
          item.className = ''
        })
      }
      if (curVal !== '') {
        this.$refs.title.map((item) => {
          if (item.innerText.match(curVal)) {
            item.parentNode.parentNode.parentNode.className = 'hightColor'
          } else {
            item.parentNode.parentNode.parentNode.style.display = 'none'
          }
        })
      }
    }
  },
  methods: {
    setClass (classname) {
      return classname
    },
    setSrc (src) {
      return src
    },
    searchNewsBtn () {
      console.log('..searchNewsBtn..')
      alert('搜索完成')
    },
    goNews () {
      console.log('goNews..')
      this.closeSearch()
      this.clearSearchContent()
    },
    goHome () {
      console.log('goHome..goHome')
      this.$router.push('/select')
    },
    clearSearchContent () {
      this.searchContent = ''
    },
    ...mapMutations([
      'toggleMenu', 'openSearch', 'closeSearch'
    ])
  }
}
</script>

<style lang="scss">
.head{
  height: 60px;
  background: #262627;
  position: relative;
  img{
    height: 50px;
    position: absolute;
    top: 5px;
    right: 20%;
    cursor: pointer;
  }
  .vNews{
    height: 20px;
    top: 20px;
    left: 10px;
  }
  .baidu{
    right: 30%;
  }
  .searchBtn{
    cursor: pointer;
    color: #FFFFFF;
    position: absolute;
    top: 18px;
    right: 15%;
  }
  .aside{
    height: 60px;
     60px;
    cursor: pointer;
    position: absolute;
    top: 0;
    right: 0;
    padding-top: 1px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    .line{
      height: 1px;
       25px;
      background: #fff;
      transition: all 0.5s;
    }
    .line:nth-of-type(2){
      margin: 6px 0;
    }
  }
  .closeMenu{
    .line:first-child{
      transform: rotate(45deg);
    }
    .line:nth-child(2){
      display: none;
    }
    .line:last-child{
      transform: rotate(-45deg);
    }
  }
  .searchPage{
    display: none;//隐藏了搜索的功能
    position: fixed;
    height: 100%;
     100%;
    overflow: scroll;
    background: #fff;
    z-index: 999;
    .header{
      height: 60px;
      display: flex;
      justify-content: space-around;
      align-items: center;
      padding: 0 2%;
      background: #262627;
      .search{
        height: 40px;
         85%;
        border: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        align-items: center;
        input{
          height: 40px;
           82%;
          color: #FFFFFF;
          text-indent: 10px;
          background-color: #262627;
          outline: none;
        }
        .icon{
          color: #FFFFFF;
          margin-right: 5%;
          -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
        }
      }
      .close{
        height: 30px;
         30px;
        color: #FFFFFF;
      }
    }
  }
  .content{
    overflow: hidden;
    background-color: #FFFFFF;
    .today{
      height: 25px;
      background: #E3E4EE;
      line-height: 25px;
      text-indent: 15px;
      font-size: 14px;
    }
    .news li{
      font-size: 16px;
      margin: 15px;
      p{
         100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      i{
        font-size: 16px;
        font-style: normal;
        margin-right: 10px;
      }
      i.isTop3{
        color: red;
      }
      span{
         90%;
        display: inline-block;
        text-overflow: ellipsis;
        overflow: hidden;
        white-space: nowrap;
      }
    }
  }
  .hightColor{
    color: #FFFFFF;
    background-color: #EEEEEE;
  }
}
</style>

/**
 * @desc 显示隐藏右侧菜单
 */
export default {
  mutations: {
    toggleMenu () {
      var page = document.querySelector('#app .page')
      var aside = document.querySelector('.head .aside')
      var pageClass = page.className
      var asideClass = aside.className
      if (pageClass === 'page') {
        page.className = 'page toggle'
      } else {
        page.className = 'page'
      }
      if (asideClass === 'aside') {
        aside.className = 'aside closeMenu'
      } else {
        aside.className = 'aside'
      }
    },
    openSearch () {
    alert('11')
      var searchPage = document.querySelector('.head .searchPage')
      searchPage.style.display = 'block'
    },
    closeSearch () {
      console.log('2222')
      var searchPage = document.querySelector('.head .searchPage')
      searchPage.style.display = 'none'
    }
  }
}

接下来我们来看app-menu组件

//menu.vue
<template>
  <section class="menus">
    <p class="user" title="">{{userName}}</p>
    <ul class="aside">
      <li v-for='(m, index) in menus' :key='index'><a href="">{{m.text}}</a></li>
    </ul>
  </section>
</template>

<script>
import { mapState } from 'vuex'
export default {
  computed: {
    ...mapState({
      userName: state => state.MenuStore.userName,
      menus: state => state.MenuStore.menus
    })
  }
}
</script>

<style lang="scss">
.menus{
  position: fixed;
  top: 0;
  right: 0;
  color: #fff;
   120px;
  height: 100%;
  background: #3C3C3D;
  z-index: 9;
  .user{
    height: 60px;
    line-height: 60px;
    background: #000;
    text-align: center;
     60%;
    padding: 0 20%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .aside li{
    height: 50px;
     60%;
    padding: 0 20%;
    cursor: pointer;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    a{
      height: 100%;
      color: #FFFFFF;
      line-height: 50px;
      display: inline-block;
    }
  }
  .aside li:hover{
    background-color: #3E90E3;
  }
}
</style>

这个menus值也是在store中存储的

//menuStore.js
/**
 * @desc 右侧菜单数据
 */
export default {
  state: {
    userName: '京州市委书记李达康',
    menus: [{
      text: '巨头'
    }, {
      text: '人物'
    }, {
      text: '电商'
    }, {
      text: '创投'
    }, {
      text: '智能硬件'
    }, {
      text: '互联网+'
    }, {
      text: 'P2P'
    }, {
      text: '前沿技术'
    }, {
      text: '游戏'
    }]
  }
}

接下来我们看nav.vue中

//nav.vue
<template>
  <section class="nav">
    <!--v-for遍历路由-->
    <div class="nav" v-for='(m, index) in menus' :key='index'>
      <router-link :to='setPaht(m.path)'><span>{{m.text}}</span></router-link>
    </div>
  </section>
</template>

<script>
export default {
  data () {
    return {
      // 路由参数
      menus: [{
        text: '精选',
        path: '/select'
      }, {
        text: '话题',
        path: '/point'
      }, {
        text: '作者',
        path: '/author'
      }]
    }
  },
  methods: {
    setPaht (path) {
      return path
    }
  }
}
</script>

<style lang="scss">
.nav{
   100%;
  height: 50px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background: #fff;
  .nav{
     33%;
    height: 100%;
    a{
      height: 100%;
       100%;
      display: flex;
      justify-content: center;
      align-items: center;
      text-decoration: none;
      color: #666;
      font-size: 18px;
      text-align: center;
      span:first-child{
        height: 30px;
         100%;
        line-height: 30px;
      }
    }
  }
  div:nth-child(2) > a > span{
    border-left: 1px solid #eee;
    border-right: 1px solid #eee;
  }
  .active{
    border-bottom: 1px solid red;
    box-sizing: border-box;
  }
}
</style>

接下来我们看select.vue

<template>
  <div class="select">
    <div class="banner">
      <swiper :options="swiperOption"  ref="mySwiper">
        <!-- 这部分放你要渲染的那些内容 -->
        <swiper-slide v-for='img in banners' :key="img.channelId">
          <img :src="setBannerSrc(img)"/>
        </swiper-slide>
        <!-- 这是轮播的小圆点 -->
        <div v-show='loadBtn' class="swiper-pagination" slot="pagination"></div>
      </swiper>
    </div>
    <!--加载动画-->
    <div class="spinner" v-show='loadAnimation'></div>
    <transition name='fade' mode='out-in'>
      <svg v-show='rocket' class="icon goTop" @click='goPageTop' aria-hidden="true">
        <use xlink:href="#icon-0028"></use>
      </svg>
    </transition>
    <section class="news">
      <div v-if='requestStatus'>
        <div v-for='(news, index) in newsDate' :key='index' :id="news.id">
          <a href="javascript: void(0)" class="new" :key='news.channelId'>
            <img v-lazy='news.imageurls[0].url' :src="setNewSrc(news.imageurls[0].url)"/>
            <div class="intro">
              <h4>{{news.title}}</h4>
              <p><span>{{news.source}}</span> | <span>{{news.pubDate}}</span></p>
            </div>
          </a>
        </div>
        <button class="loadMore" @click='loadMoreBtn' v-show='loadBtn'>点击加载更多</button>
      </div>
      <div class="fail" v-else>/(ㄒoㄒ)/~~, 请求到数据失败!</div>
    </section>
  </div>
</template>

<script>
// 导入轮播图组件
import { swiper, swiperSlide } from 'vue-awesome-swiper'
// 导入vuex
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
  data () {
    return {
      rocket: false,
      requestStatus: true,
      swiperOption: {
        pagination: '.swiper-pagination',
        slidesPerView: 'auto',
        centeredSlides: true,
        paginationClickable: true,
        spaceBetween: 30,
        onSlideChangeEnd: swiper => {
          // 这个位置放swiper的回调方法
          this.page = swiper.realIndex + 1
          this.index = swiper.realIndex
        }
      }
    }
  },
  computed: {
    /**
     * @desc 从store中引入需要的数据
     */
    ...mapState({
      page: state => state.SelectStore.page,
      newsUrl: state => state.SelectStore.newsUrl,
      banners: state => state.SelectStore.banners,
      newsDate: state => state.SelectStore.newsDate,
      loadBtn: state => state.SelectStore.loadBtn,
      pathName: state => state.SelectStore.pathName,
      loadAnimation: state => state.SelectStore.loadAnimation
    })
  },
  created: function () {
    this.askNews(this.newsUrl + this.page) // 第一次加载请求数据
    let _this = this
    /**
     * @desc 判断是否显示回到顶部的火箭图标
     */
    window.onscroll = function () {
      let leaveTop = document.body.scrollTop
      if (leaveTop > 600) {
        _this.rocket = true
      } else {
        _this.rocket = false
      }
    }
    console.log(`%c ${this.$store.state.slogan}`,"font-size: 22px;color:#00BBEE", "Copyright © 2019");
  },
  methods: {
    ...mapActions([
      'askNews', 'setSrc'
    ]),
    ...mapMutations([
      'loadMore'
    ]),
    /**
     * @desc 设置轮播图地址
     */
    setBannerSrc (src) {
      return src
    },
    /**
     * @desc 设置新闻图片地址
     */
    setNewSrc (url) {
      return url
    },
    /**
     * @desc 加载更多
     */
    loadMoreBtn () {
      this.loadMore()
      this.askNews(this.newsUrl + this.page)
    },
    /**
     * @desc 回到顶部
     */
    goPageTop () {
      document.body.scrollTop = 0
    }
  },
  components: {
    swiper,
    swiperSlide
  }
}
</script>

<style lang="scss">
.select{
  background: #fff;
  .swiper-wrapper{
    height: 200px;
    .swiper-slide img{
       100%;
      height: 200px;
    }
  }
  .news{
    min-height: 500px;
    padding: 0 10px;
    .new{
      height: 100px;
      color: #262627;
      border-bottom: 1px solid #eee;
      display: flex;
      justify-content: flex-start;
      align-items: center;
       100%;
      img{
        height: 80px;
         100px;
      }
      .intro{
         80%;
        height: 80px;
        display: flex;
        padding-left: 10px;
        flex-direction: column;
        justify-content: space-between;
        h4{
          font-size: 20px;
          line-height: 1.2;
          font-weight: bold;
          overflow: hidden;
          display: -webkit-box;
          word-break: break-all;
          -webkit-line-clamp: 2;
          text-overflow: ellipsis;
          -webkit-box-orient: vertical;
        }
        p{
          font-size: 13px;
          color: #666;
        }
      }
    }
    .loadMore{
      height: 50px;
       100%;
      color: #545454;
      background: #eee;
      text-align: center;
      line-height: 50px;
      font-size: 13px;
      border: none;
      border-radius: 0;
      outline: none;
      margin-bottom: 10px;
    }
    .fail{
      display: flex;
      min-height: 300px;
      align-items: center;
      justify-content: center;
    }
  }
}
/*加载动画*/
.spinner {
  position: fixed;
  left: 40%;
  bottom: 10%;
   80px;
  height: 80px;
  margin: 50px auto;
  background-color: #69C61D;
  border-radius: 100%;
  -webkit-animation: scaleout 1.0s infinite ease-in-out;
  animation: scaleout 1.0s infinite ease-in-out;
}
@-webkit-keyframes scaleout {
  0% { -webkit-transform: scale(0.0) }
  100% {
    -webkit-transform: scale(1.0);
    opacity: 0;
  }
}
@keyframes scaleout {
  0% {
    transform: scale(0.0);
    -webkit-transform: scale(0.0);
  } 100% {
    transform: scale(1.0);
    -webkit-transform: scale(1.0);
    opacity: 0;
  }
}
.goTop{
  color: #50BFFF;
  position: fixed;
  bottom: 40px;
  right: 20px;
  z-index: 9999;
  cursor: pointer;
   60px;
  height: 60px;
  transition: all 1s ease;
  transform: scale(0.6);
}
.goTop:active{
  color: #C40000;
  transform: scale(1);
}
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.8s
}
.fade-enter, .fade-leave-active {
  opacity: 0
}
.tip{
   100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  a{
    color: #19C8A9;
  }
  span{
    margin-bottom: 10px;
  }
}
</style>

加载更多的代码也有在store中定义

//selectStore.js
import axios from 'axios'
import $ from 'webpack-zepto'
export default {
  state: {
    page: 1,
    data: [],
    newsDate: [],
    banners: [],
    searchNews: [],
    loadBtn: false,
    loadAnimation: true,
    newsUrl: 'https://route.showapi.com/109-35?showapi_appid=34477&showapi_sign=cfa5957a730f43d38886bd16469b2a86&channelId=5572a108b3cdc86cf39001cd&needContent=0&needHtml=1&page='
  },
  mutations: {
    /**
     * @desc 加载新闻
     */
    loadNews (state) {
      let data = state.data
      if (data.length > 2) { // 判断数据是否存在
        for (var i = 0; i < data.length; i++) {
          if (data[i].imageurls[0]) {
            state.newsDate.push(data[i])
          }
        }
        for (let i = 0; i < 4; i++) {
          if (state.banners.length < 4) {
            state.banners.push(state.newsDate[i].imageurls[0].url)
          }
        }
      } else {
        state.loadAnimation = false
        console.log('没有更多数据了')
        return false
      }
      // 数据请求成功显示加载更多按钮
      state.loadBtn = true
      state.loadAnimation = false
    },
    /**
     * @desc 点击加载更多
     */
    loadMore (state) {
      state.page++
      state.loadAnimation = true
    },
    updatePathName (state, newPathName) {
      state.pathName = newPathName
    }
  },
  actions: {
    /**
     * @desc axios异步请求函数 类似jquery的ajax方法
     */
    askNews ({commit, state}, url) {
      axios({
        method: 'get',
        url: url
      })
        .then((res) => {
          if (res.data.showapi_res_code !== -2) {
            let data = res.data.showapi_res_body.pagebean.contentlist
            state.data = data
            for (let i in data) {
              state.searchNews.push({title: data[i].title, id: data[i].id})
            }
          } else {
            var tips = `<p class="tip">
              <span>接口请求已达上限 /(ㄒoㄒ)/~~!!!</span>
              <span>选择和AI聊天,缓解失望的心情吧:</span>
              <a href="http://lx.openspeech.cn/auth/project/ai_niu/index.html">快来和我聊天!</a>
            </p>`
            $(tips).appendTo('.news')
          }
          commit('loadNews')
        })
    }
  }
}

接下来看话题页面

其实我很好奇,为什么把数据写在store中

//pointStore.js
/**
 * @desc 话题页面数据
 */
export default {
  state: {
    points: [{
      times: '609期',
      title: '提高个税起征点真能减负嘛?',
      msg: '3月7日,财政部部长肖捷在发布会上表示,个人所得税免征额将根据消费水平综合测算,...'
    }, {
      times: '609期',
      title: '提高个税起征点真能减负嘛?',
      msg: '3月7日,财政部部长肖捷在发布会上表示,个人所得税免征额将根据消费水平综合测算,...'
    }, {
      times: '609期',
      title: '提高个税起征点真能减负嘛?',
      msg: '3月7日,财政部部长肖捷在发布会上表示,个人所得税免征额将根据消费水平综合测算,...'
    }]
  }
}
<template>
  <section class="point">
      <div class='question'>
        <div class="mask">
          <h2>热点城市房价会否持续疯涨?</h2>
          <p>610期</p>
          <div class="result">
            <p><span>正方</span><span>反方</span></p>
            <progress value="75" max="100"></progress>
            <p><span>75%</span><span>25%</span></p>
          </div>
          <a class="join" href="javascript: void(0)">进入专题></a>
        </div>
      </div>
      <div class="prev">
        <div v-for='(p, index) in points' :key='index' class="list">
          <div class="mask">
            <span class="times">{{p.times}}</span>
            <h2>{{p.title}}</h2>
             <p>{{p.msg}}</p>
             <a class="join" href="javascript: void(0)"> > </a>
          </div>
        </div>
      </div>
  </section>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'point',
  computed: {
    ...mapState({
      points: state => state.PointStore.points
    })
  }
}
</script>

<style lang="scss">
.point{
  color: #fff;
  background: #fff;
  .question{
    height: 180px;
    background: url(../assets/point/pointBg.jpg);
    background-size: 100% 100%;
    .mask{
      display: flex;
      justify-content: center;
      flex-direction: column;
      align-items: center;
      height: 100%;
       100%;
      position: relative;
      background: rgba(0,0,0,0.6);
      h2{
        font-size: 20px;
        font-weight: bold;
      }
      p{
        font-size: 16px;
        margin: 10px 0;
      }
      .result{
        height: 100px;
         80%;
          p{
            display: flex;
            justify-content: space-between;
          }
          progress {
             100%;
            background-color:#005588;
            color: #E94C3D; /*IE10*/
          }
          progress::-moz-progress-bar { background: #E94C3D; }
          progress::-webkit-progress-bar { background: #005588; }
          progress::-webkit-progress-value  { background: #E94C3D; }
      }
      .join{
        color: #E94C3D;
        position: absolute;
        bottom: 10px;
        right: 10px;
      }
    }
  }
  .prev{
    padding: 10px;
    .list{
      height: 150px;
       100%;
      margin-bottom: 10px;
      overflow: hidden;
      background: url(../assets/point/pointBg.jpg) no-repeat;
      background-size: 100%;
      .mask{
        display: flex;
        justify-content: center;
        flex-direction: column;
        align-items: center;
        height: 100%;
         100%;
        background: rgba(0,0,0,0.6);
        position: relative;
        h2{
          font-size: 20px;
          font-weight: bold;
           100%;
          text-align: center;
          padding: 0 20px;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
        p{
          color: #eee;
          margin: 10px 20px;
          line-height: 1.3;
          font-size: 13px;
          overflow: hidden;
          display: -webkit-box;
          word-break: break-all;
          -webkit-line-clamp: 2;
          text-overflow: ellipsis;
          -webkit-box-orient: vertical;
        }
        .times{
          position: absolute;
          left: 0;
          top: 0;
          color: #eee;
          display: inline-block;
          padding: 2px;
          font-size: 13px;
        }
        .join{
          position: absolute;
          right: 5px;
          bottom: 0;
          color: #eee;
          color: #E94C3D;
          display: inline-block;
          padding: 10px;
          font-size: 13px;
        }
      }
    }
  }
}
</style>

接下来是author页面

<template>
  <section class="author">
    <button class="goHome" @click='goHome'>Home</button>
    <!--<a v-link="select">select</a>-->
    <div class="banner">
      <div v-for='(a, index) in author' :key='index' class="item">
        <div class="msg">
          <img :src="setAutherSrc(a.src)"/>
          <p class="name">{{a.name}}</p>
          <p class="slogan">{{a.slogan}}</p>
        </div>
        <span class="focus" v-if='a.status' @click='a.status = !a.status'>关注</span>
        <span class="focus focused" @click='a.status = !a.status' v-else>已关注</span>
      </div>
    </div>
    <section class="more">
      <div v-for='(o, index) in other' :key='index' class="other">
        <div class="authorMsg">
          <img :src="setOtherSrc(o.src)"/>
          <div class="intro">
            <p class="name">{{o.name}}</p>
            <p class="slogan">{{o.slogan}}</p>
          </div>
        </div>
        <div>
          <span class="focus" v-if='o.status' @click='o.status = !o.status' >关注</span>
          <span class="focus focused" @click='o.status = !o.status' v-else>已关注</span>
        </div>
      </div>
    </section>
  </section>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'author',
  computed: {
    ...mapState({
      author: state => state.AuthorStore.author,
      other: state => state.AuthorStore.other,
      pathName: state => state.SelectStore.pathName
    })
  },
  watch: {
    '$route': 'fetchData'
  },
  methods: {
    setAutherSrc (src) {
      return src
    },
    setOtherSrc (src) {
      return src
    },
    fetchData () {
      location.hash = 'select'
      console.log(this.$route)
    },
    goHome () {
      if (history.length) {
        this.$router.go(parseFloat(-this.$store.state.historyLength) + 1)
      }
    }
  },
  beforeRouteEnter (to, from, next) {
    next()
  },
  beforeRouteUpdate (to, from, next) {
    console.log(9)
    next()
  },
  beforeRouteLeave (to, from, next) {
    next()
  }
}
</script>

<style lang="scss">
.author{
  background: #fff;
  border-right: 1px solid #666;
  .banner{
    background: url(../assets/author/author_banner_bg.jpg);
    .item{
      height: 210px;
       45%;
      margin: 2%;
      color: #fff;
      opacity: 0.9;
      display: inline-block;
      text-align: center;
      background: #2E2E2F;
      .msg{
        display: flex;
        flex-direction: column;
        background: #262627;
        padding: 10px 0;
        justify-content: space-around;
        align-items: center;
        img{
          height: 100px;
           100px;
          border-radius: 50%;
        }
        .name{
          margin: 10px 0;
        }
        .slogan{
          color: #666;
           80%;
          font-size: 13px;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
      }
      .focus{
         80px;
        font-size: 13px;
        display: inline-block;
        padding: 4px 0;
        background: #E94C3D;
        border-radius: 10px;
        cursor: pointer;
        margin: 10px 0;
        -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
      }
      .focused{
        background: #262627;
      }
    }
  }
  .more{
    padding: 10px;
    .other{
      height: 100px;
      background: #eee;
      display: flex;
      margin-bottom: 10px;
      justify-content: space-between;
      padding: 0 10px;
      align-items: center;
      .authorMsg{
        display: flex;
        justify-content: space-between;
        align-items: center;
        img{
          height: 80px;
           80px;
          border-radius: 50%;
        }
        .intro{
          height: 40px;
          margin: 0 10px;
          display: flex;
          flex-direction: column;
          justify-content: space-between;
          .slogan{
            font-size: 13px;
            color: #666;
          }
        }
      }
      .focus{
        display: inline-block;
         80px;
        color: #fff;
        cursor: pointer;
        font-size: 13px;
        text-align: center;
        padding: 5px 0;
        background: #D5483A;
        border-radius: 15px;
        margin-left: 10px;
        -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
      }
      .focused{
        background: #262627;
      }
    }
  }
  .goHome{
    position: fixed;
    right: 15px;
    bottom: 30px;
     50px;
    height: 50px;
    border-radius: 50%;
    border: none;
    outline: none;
    color: #FFFFFF;
    background: rgba(0,0,0,0.5);
  }
}
</style>

AuthorStore.js中进入到对应的页面也是在state中

//authodStore.js
/**
 * @desc 作者页面数据
 */
export default {
  state: {
    author: [{
      src: require('../assets/author/author.jpg'),
      name: '变革家',
      slogan: '帮股权投资者把好第一关!',
      status: true
    }, {
      src: require('../assets/author/author.jpg'),
      name: '懂懂笔记',
      slogan: '20年国内财经媒体从业记录者!',
      status: true
    }, {
      src: require('../assets/author/author.jpg'),
      name: '变革家',
      slogan: '帮股权投资者把好第一关!',
      status: true
    }, {
      src: require('../assets/author/author.jpg'),
      name: '娱乐硬糖',
      slogan: '有温度的泛娱乐产业自媒体。!',
      status: true
    }],
    other: [{
      src: require('../assets/author/author.jpg'),
      name: '邝新华',
      slogan: '新周刊主笔',
      status: true
    }, {
      src: require('../assets/author/author.jpg'),
      name: '40秒',
      slogan: '认识最疯狂的天才',
      status: true
    }, {
      src: require('../assets/author/author.jpg'),
      name: '艾问iAsk',
      slogan: '科技商业价值发现者',
      status: true
    }]
  }
}

最后就是router.js

import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'
import Select from '../components/Select'
import Point from '../components/Point'
import Author from '../components/Author'

Vue.use(Router)

const router = new Router({
  linkActiveClass: 'active',
  hashbang: true, // 将路径格式化为#!开头
  history: true, // 启用HTML5 history模式,可以使用pushState和replaceState来管理记录
  /**
   * @desc 路由配置
   */
  routes: [
    {
      path: '/select',
      component: Select
    }, {
      path: '/point',
      component: Point
    }, {
      path: '/author',
      component: Author,
      meta: { requiresAuth: true }
    }, {
      path: '/*',
      component: Select
    }
  ]
})
/**
 * @desc 全局监听路由变化
 */
router.beforeEach((to, from, next) => {
  store.dispatch('updateHistoryLength') // 变化时更新路由切换长度
  next()
})

export default router

其实不太理解作者写的这个路由的切换次数
···js
/**

  • @desc 导入需要的store
  • @author wtniu
    */
    import Vue from 'vue'
    import Vuex from 'vuex'
    import SelectStore from './SelectStore'
    import PointStore from './PointStore'
    import AuthorStore from './AuthorStore'
    import MenuStore from './MenuStore'
    import HeadStore from './HeadStore'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
slogan: '叩首为梦 码梦为生!',
historyLength: 0
},
mutations: {
/*
* @desc 记录路由切换次数
* @arg {object} state 状态
*/
updateHistoryLength (state) {
state.historyLength++
}
},
actions: {
updateHistoryLength ({commit}) {
commit('updateHistoryLength')
}
},
modules: {
SelectStore,
PointStore,
AuthorStore,
MenuStore,
HeadStore
}
})

作者大大的这样项目很值得推荐的,将数据放在store的state中这种方法很很新颖,很喜欢,推荐
原文地址:https://www.cnblogs.com/smart-girl/p/13295983.html