【珍惜時間】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>聯繫我:[email protected]</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;
    width: 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;
      width: 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%;
    width: 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;
        width: 85%;
        border: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        align-items: center;
        input{
          height: 40px;
          width: 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;
        width: 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{
        width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      i{
        font-size: 16px;
        font-style: normal;
        margin-right: 10px;
      }
      i.isTop3{
        color: red;
      }
      span{
        width: 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;
  width: 120px;
  height: 100%;
  background: #3C3C3D;
  z-index: 9;
  .user{
    height: 60px;
    line-height: 60px;
    background: #000;
    text-align: center;
    width: 60%;
    padding: 0 20%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .aside li{
    height: 50px;
    width: 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{
  width: 100%;
  height: 50px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background: #fff;
  .nav{
    width: 33%;
    height: 100%;
    a{
      height: 100%;
      width: 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;
        width: 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{
      width: 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;
      width: 100%;
      img{
        height: 80px;
        width: 100px;
      }
      .intro{
        width: 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;
      width: 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%;
  width: 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;
  width: 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{
  width: 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%;
      width: 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;
        width: 80%;
          p{
            display: flex;
            justify-content: space-between;
          }
          progress {
            width: 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;
      width: 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%;
        width: 100%;
        background: rgba(0,0,0,0.6);
        position: relative;
        h2{
          font-size: 20px;
          font-weight: bold;
          width: 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;
      width: 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;
          width: 100px;
          border-radius: 50%;
        }
        .name{
          margin: 10px 0;
        }
        .slogan{
          color: #666;
          width: 80%;
          font-size: 13px;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
      }
      .focus{
        width: 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;
          width: 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;
        width: 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;
    width: 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中這種方法很很新穎,很喜歡,推薦
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章