項目--3

一、回顧

二、token校驗登陸 ---- day06/myapp

cnpm i jsonwebtoken -S 安裝 token 模塊css

2.1 當登陸成功時 生成token,給前端返回token

var jwt = require('jsonwebtoken');
// 實現登錄功能
router.post('/login', (req, res, next) => {
  // 一、獲取表單信息
  let { tel, password } = req.body;
  // 二、依據手機號查詢有沒有該用戶
  sql.find(User, { tel }, { _id: 0 }).then(data => {
    // 2.1 判斷有麼有該用戶
    if (data.length === 0) {
      // 2.2 沒有該用戶
      res.send(utils.unregister)
    } else {
      // 2.3 有該用戶,驗證密碼
      // 2.3.1 獲取數據庫中的密碼
      let pwd = data[0].password;
      // 2.3.2 比較 輸入的 密碼和數據庫中的密碼
      var flag = bcrypt.compareSync(password, pwd) // 前爲輸入,後爲數據庫
      if (flag) {
        // 2.3.3 密碼正確,生成token
        let userid = data[0].userid
        let token = jwt.sign({ userid }, 'daxunxun', {
          // expiresIn: 60*60*24// 受權時效24小時
          expiresIn: 60*0.1// 受權時效6s
        })
        res.send({
          code: '10010',
          message: '登錄成功',
          token: token
        })
      } else {
        // 2.3.4 密碼錯誤
        res.send({
          code: '10100',
          message: '密碼錯誤'
        })
      }
    }
  })
})

2.2 全局校驗token,若是沒有token信息,表示未登陸須要從新登錄以後再操做 app.js

var jwt = require('jsonwebtoken');

app.use((req, res, next) => {
  // 一、若是不是登錄和註冊頁面
  if (req.url !== '/users/login' && req.url !== '/users/register') {
    // 1.1 獲取前端提交的token信息(get/post/頭信息)
    let token = req.headers.token || req.query.token || req.body.token;
    if (token) {
      // 1.2 若是存在token,校驗token
      jwt.verify(token, 'daxunxun', function(err, decoded) {
        if (err) {
          // 1.2.1 若是校驗token失敗,返回字段
          res.send({ 
            code: '10119', 
            message: '沒有找到token.' 
          });
        } else {
          // 1.2.2 若是校驗token成功,繼續操做接口
          req.decoded = decoded;  
          console.log('驗證成功', decoded);
          next()
        }
      }) 
    } else {
      // 1.3 若是沒有傳遞token,返回字段
      res.send({ 
        code: '10119', 
        message: '沒有找到token.' 
      });
    }
  } else {
    // 二、若是是登錄和註冊,繼續操做
    next()
  }
})

三、登錄功能

3.1 建立登錄頁面 views/login/index.vue

<template>
  <div class="box">
    <header class="header">登錄</header>
    <div class="content">
      <input type="text" placeholder="手機號碼" v-model="tel">
      <p class="tip">{{ teltip }}</p>
      <input type="password" placeholder="密碼" v-model="password">
      <p class="tip">{{ passwordtip }}</p>
      <button class="userBtn" @click="login">登錄</button>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      tel: '18813007814',
      password: '123456'
    }
  },
  computed: {
    teltip () {
      return '手機號碼格式錯誤'
    },
    passwordtip () {
      return '密碼格式錯誤'
    }
  },
  methods: {
    login () {

    }
  }
}
</script>
<style lang="scss">
input {
  outline: none;
  border: 0;
  width: 96%;
  margin: 5px 2%;
  border-bottom: 1px solid #efefef;
  text-indent: 10px;
  display: block;
  line-height: 36px;
}
.tip {
  text-align: center;
  color: #f66;
  height: 20px;
}
.userBtn {
  outline: none;
  border: 0;
  display: block;
  background-color:#f66;
  width: 96%;
  margin: 15px 2%;
  line-height: 40px;
  font-size: 18px;
  color: #fff;
}
</style>

3.2 添加登錄的路由 router/index.js

{
  path: '/login',
  name: 'login',
  components: { // 一個路由 對象兩個位置發生變化
    default: () => import('@/views/login/index.vue')
  }
}

瀏覽器輸入 /login 查看效果html

3.3 檢驗手機號和密碼

computed: {
  teltip () {
    if (this.tel === '') {
      return ''
    } else if (this.tel.length !== 11) {
      return '手機號碼格式錯誤'
    } else {
      return ''
    }
  },
  passwordtip () {
    if (this.password === '') {
      return ''
    } else if (this.password.length < 6) {
      return '密碼格式錯誤'
    } else {
      return ''
    }
  }
},

3.4 登錄

methods: {
  login () {
    if (this.tel === '' || this.teltip !== '') {
      this.tip = '手機號格式錯誤'
      return
    }
    if (this.password === '' || this.passwordtip !== '') {
      this.tip = '密碼格式錯誤'
      return
    }
    // 登錄
    axios.post('/users/login', {
      tel: this.tel,
      password: this.password
    }).then(res => {
      console.log(res.data)
      /***
        * 10086 未註冊
        * 10100 密碼錯誤
        * 10010 登錄成功
        */
      if (res.data.code === '10086') {
        this.tip = '該用戶未註冊,請先註冊'
      } else if (res.data.code === '10100') {
        this.tip = '密碼錯誤'
      } else {
        // 此時爲登錄成功,獲取token信息存入本地
        this.tip = ''
        const token = res.data.token
        localStorage.setItem('token', token)
      }
    })
  }
}

四、修改首頁的請求、詳情頁面的請求,給每個請求添加參數token

4.1 首頁面

  • 結構 ---- 登錄顯示列表,未登陸顯示 登錄提示信息
<div class="content">
  <!-- 使用組件 -->
  <Prolist v-if="flag" :prolist="prolist"/>
  <div v-else>
    登錄以後才能看到更多的信息
    <router-link to="/login">登錄</router-link>
  </div>
</div>
  • 行爲
data () {
  return {
    prolist: [],
    flag: false
  }
},
created () {
  axios.get('/pro?token=' + localStorage.getItem('token')).then(res => {
    console.log(res.data)
    if (res.data.code === '10119') {
      this.flag = false
    } else {
      this.flag = true
      this.prolist = res.data.data
    }
  })
}

4.2 詳情頁面

  • 結構
<div class="content">
  <div v-if="flag">
    <img :src="proimg" alt="">
    <h1>{{ proname }}</h1>
    <h3>{{ note }}</h3>
    <p>{{ price }}</p>
    <ul>
      <li v-for="item of commentlist" :key="item.commentid">
        <h4>{{ item.username }} - {{ item.rating }}</h4>
        <p>{{ item.note }}</p>
      </li>
    </ul>
  </div>
  <div v-else>
    登錄以後才能看到更多的信息
    <router-link to="/login">登錄</router-link>
  </div>
</div>
  • 行爲
data () {
  return {
    flag: false,
    proid: '',
    proname: '',
    proimg: '',
    price: '',
    note: '',
    commentlist: []
  }
},
created () {
  console.log(this.$route.query)
  const proid = this.$route.query.proid
  axios.get('/pro/detail?proid=' + proid + '&token=' + localStorage.getItem('token')).then(res => {
    console.log(res.data)
    if (res.data.code === '10119') {
      this.flag = false
    } else {
      this.flag = true
      this.proid = res.data.data.proid
      this.proname = res.data.data.proname
      this.proimg = res.data.data.proimg
      this.price = res.data.data.price
      this.note = res.data.data.note
    }
  })
  axios.get('/comment?proid=' + proid + '&token=' + localStorage.getItem('token')).then(res => {
    // console.log(res.data.data)
    if (res.data.code === '10119') {
      this.flag = false
    } else {
      this.flag = true
      this.commentlist = res.data.data
    }
  })
}

五、使用UI庫

  • PC element-ui iview
  • 移動端 vant mint-ui

找到UI庫API文檔,安裝模塊,找到快速上手,按照步驟配置UI庫,複製-粘貼-刪除-修改前端

以vant爲例: https://youzan.github.io/vant/#/zh-CN/introvue

5.1 安裝依賴

cnpm i vant -Sios

5.2 配置UI庫 -- 按需引入

cnpm i babel-plugin-import -Dgit

修改babel.config.js文件es6

module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ],
  // +++++++++++++++++
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
}

配置文件的修改,必定要記得從新啓動服務器github

六、使用UI庫的 輪播圖 構建首頁的輪播圖

https://youzan.github.io/vant/#/zh-CN/swipeweb

  • 獲取輪播圖若是不須要校驗登錄狀態,須要更改後臺的代碼 app.js中過濾掉
app.use((req, res, next) => {
  if (req.url !== '/users/login' && req.url !== '/users/register' && req.url !== '/banner') {}
})

6.1 首頁輪播圖

  • 首頁引入 輪播圖組件
import Vue from 'vue'
import { Swipe, SwipeItem } from 'vant'
Vue.use(Swipe).use(SwipeItem) // 使用該語句,組件就無需再單獨註冊
  • 請求輪播圖數據
data () {
  return {
    bannerlist: [], // *************
    prolist: [],
    flag: false
  }
},
created () {
  // ++++++++++++++++++++
  axios.get('/banner').then(res => {
    console.log(res.data)
    this.bannerlist = res.data.data
  })
  axios.get('/pro?token=' + localStorage.getItem('token')).then(res => {
    console.log(res.data)
    if (res.data.code === '10119') {
      this.flag = false
    } else {
      this.flag = true
      this.prolist = res.data.data
    }
  })
}
  • 渲染數據
<!-- 輪播圖 -->
<van-swipe :autoplay="3000" indicator-color="white">
  <van-swipe-item v-for="item of bannerlist" :key="item.bannerid">
    <img :src="item.img" alt="">
  </van-swipe-item>
</van-swipe>
  • 依據需求修改相應的樣式

七、商品詳情頁添加商品導航組件

https://youzan.github.io/vant/#/zh-CN/goods-actionsql

  • 引入組件

  • 修改

八、地址選擇

views/address/index.vue + 路由

{
  path: '/address',
  name: 'address',
  components: { // 一個路由 對象兩個位置發生變化
    default: () => import('@/views/address/index.vue')
  }
}
<template>
  <div class="box">
    <header class="header">城市選擇</header>
    <div class="content">
      <van-index-bar>
        <van-index-anchor v-for="(item, index) of list" :key="index" :index="item.letter" >
          <van-cell v-for="itm of item.cities" :key="itm.id" :title="itm.name" />
        </van-index-anchor>
      </van-index-bar>
    </div>
  </div>
</template>

<script>
import Vue from 'vue'
import { IndexBar, IndexAnchor, Cell } from 'vant'
import axios from 'axios'
Vue.use(IndexBar).use(IndexAnchor)
Vue.use(Cell)
export default {
  data () {
    return {
      list: []
    }
  },
  created () {
    axios.get('/city.json').then(res => {
      console.log(res.data)
      this.list = res.data
    })
  }
}
</script>

<style lang="scss" scoped>
.van-index-bar {
  height: 100%;
  overflow: auto;
}
</style>

九、上拉加載

https://youzan.github.io/vant/#/zh-CN/list

  • 引入組件
import axios from 'axios'
import Vue from 'vue'
import { Swipe, SwipeItem, List } from 'vant' // ++++++
// 引入列表的組件  ---- es6中的模塊化
import Prolist from '@/components/Prolist.vue'
Vue.use(Swipe).use(SwipeItem)
Vue.use(List) // ++++++
  • List 組件經過loading和finished兩個變量控制加載狀態,當組件滾動到底部時,會觸發load事件並將loading設置成true。此時能夠發起異步操做並更新數據,數據更新完畢後,將loading設置成false便可。若數據已所有加載完畢,則直接將finished設置成true便可。
data () {
  return {
    bannerlist: [],
    prolist: [],
    flag: false,
    // ++++++++
    loading: false, // 表示當前是否是正在加載,若是爲真,表示能夠請求數據,請求成功置爲false
    // ++++++++
    finished: false, // 爲true表示全部數據都已加載完畢
    // +++++++++
    pageCode: 1 // 頁碼 從1開始,默認值爲0
  }
},
methods: {
  onLoad () { // 頁面觸底 觸發該函數,能夠加載下一頁的數據
    this.loading = true // 開始加載數據
    axios.get('/pro?limitNum=10&pageCode=' + this.pageCode + '&token=' + localStorage.getItem('token')).then(res => {
      console.log(res.data)
      this.loading = false // 表示加載結束
      this.pageCode++ // 加載結束 頁碼加1
      if (res.data.code === '10119') { // 未登陸
        this.flag = false
      } else { // 能夠拿到數據
        this.flag = true
        // 判斷有沒有數據,若是沒有數據,告訴沒有數據了,若是有數據,拼接數據
        if (res.data.data.length === 0) {
          this.finished = true // 表示數據已經加載完畢
        } else {
          // 拼接數據 ----- 數組的合併
          // arr.concat(arr1)
          // [...arr, ...arr1]  es6中的合併數組
          this.prolist = [...this.prolist, ...res.data.data]
        }
      }
    })
  }
}
  • 結構中使用
<!-- 使用組件 -->
  <van-list
    v-model="loading"
    :finished="finished"
    finished-text="沒有更多了"
    @load="onLoad"
  >
    <Prolist v-if="flag" :prolist="prolist"/>
    <div v-else>
      登錄以後才能看到更多的信息
      <router-link to="/login">登錄</router-link>
    </div>
  </van-list>

10 下拉刷新

https://youzan.github.io/vant/#/zh-CN/pull-refresh

  • 引入組件
import axios from 'axios'
import Vue from 'vue'
import { Swipe, SwipeItem, List, PullRefresh } from 'vant' // ++++++
// 引入列表的組件  ---- es6中的模塊化
import Prolist from '@/components/Prolist.vue'
Vue.use(Swipe).use(SwipeItem)
Vue.use(List)
Vue.use(PullRefresh) // ++++++
  • js
data () {
  return {
    bannerlist: [],
    prolist: [],
    flag: false,
    loading: false, // 表示當前是否是正在加載,若是爲真,表示能夠請求數據,請求成功置爲false
    finished: false, // 爲true表示全部數據都已加載完畢
    pageCode: 1,
    // ++++++++++++++
    isLoading: false // 在不在刷新,若是爲真,能夠請求數據,請求完畢設置爲false
  }
},
methods: {
  onLoad () { // 頁面觸底 觸發該函數,能夠加載下一頁的數據
    this.loading = true // 開始加載數據
    axios.get('/pro?limitNum=10&pageCode=' + this.pageCode + '&token=' + localStorage.getItem('token')).then(res => {
      console.log(res.data)
      this.loading = false // 表示加載結束
      this.pageCode++ // 加載結束 頁碼加1
      if (res.data.code === '10119') { // 未登陸
        this.flag = false
      } else { // 能夠拿到數據
        this.flag = true
        // 判斷有沒有數據,若是沒有數據,告訴沒有數據了,若是有數據,拼接數據
        if (res.data.data.length === 0) {
          this.finished = true // 表示數據已經加載完畢
        } else {
          // 拼接數據 ----- 數組的合併
          // arr.concat(arr1)
          // [...arr, ...arr1]  es6中的合併數組
          this.prolist = [...this.prolist, ...res.data.data]
        }
      }
    })
  },
  // +++++++++++++++++++++++++
  onRefresh () { // 下拉觸發此函數
    this.isLoading = true // 表示能夠請求第一頁(默認)的數據
    axios.get('/pro?token=' + localStorage.getItem('token')).then(res => {
      console.log(res.data)
      this.isLoading = false // 表示下拉刷新請求函數結束
      if (res.data.code === '10119') {
        this.flag = false
      } else {
        this.flag = true
        this.finished = false // 表示還能夠繼續上拉加載
        this.pageCode = 1 // 下拉刷新即加載第一頁數據,刷新以後重置頁碼
        this.prolist = res.data.data // 下拉刷新就是直接替換列表
      }
    })
  }
}
  • 結構 使用下拉刷新組件所有包含 content內容區域
<div class="content">
  <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
    <!-- 輪播圖 -->
    <van-swipe :autoplay="3000" indicator-color="white">
      <van-swipe-item v-for="item of bannerlist" :key="item.bannerid">
        <img :src="item.img" alt="">
      </van-swipe-item>
    </van-swipe>
    <!-- 使用組件 -->
    <van-list
      v-model="loading"
      :finished="finished"
      finished-text="沒有更多了"
      @load="onLoad"
    >
      <Prolist v-if="flag" :prolist="prolist"/>
      <div v-else>
        登錄以後才能看到更多的信息
        <router-link to="/login">登錄</router-link>
      </div>
    </van-list>
  </van-pull-refresh>
</div>

十一、回到頂部功能

誰有滾動條,誰的scrollTop爲0,跟佈局相關

彈性盒佈局 -- content -- 產生滾動條

11.1 準備小圖標,更新public/index.html的css的值

//at.alicdn.com/t/font_1476238_uph8zgimp3.css

11.2 固定定位返回頂部小圖標

<span class="backtop iconfont icon-fanhuidingbu"></span>

.backtop {
  position: fixed;
  bottom: 60px;
  right: 10px;
  font-size: 30px;
}

11.3 添加點擊返回頂部事件

<div class="content" id="content"></div>
 <span @click="backtop" class="backtop iconfont icon-fanhuidingbu"></span>

  backtop () {
    document.getElementById('content').scrollTop = 0
  }

11.4 滾動到必定距離纔出現返回頂部按鈕

<span @click="backtop" v-show="topflag" class="backtop iconfont icon-fanhuidingbu"></span>
data () {
  return {
    bannerlist: [],
    prolist: [],
    flag: false,
    loading: false, // 表示當前是否是正在加載,若是爲真,表示能夠請求數據,請求成功置爲false
    finished: false, // 爲true表示全部數據都已加載完畢
    pageCode: 1,
    isLoading: false, // 在不在刷新,若是爲真,能夠請求數據,請求完畢設置爲false
    // +++++++++++++
    topflag: false // 默認不顯示返回頂部圖標
  }
},
watch: {
  pageCode (newval, oldval) {
    if (newval > 2) { // 由於剛開始會默認加載一次數據,加載完以後頁碼已爲2
      this.topflag = true
    } else {
      this.topflag = false
    }
  }
},
methods: {
  backtop () {
    document.getElementById('content').scrollTop = 0
    this.topflag = false // ++++++++++++++++++++
  }
}
相關文章
相關標籤/搜索