Vue.js+Laravel 前後端分離的多表多使用者登陸課題(三)

Sloth發表於2020-02-05

接續於 = >《Laravel 多使用者多表登陸的第三方擴充套件包 passport-multiauth(二)》


Vue CLI 建立新專案 multiauthvue

 vue create multiauthvue
 npm run serve 後可見如下圖歡迎頁面

Vue全家桶+Laravel前後端分離的多表多使用者登陸課題(x)

因為已經確定了要使用Vue全家桶Vue Router,Vuex,另外需要 HTTP 客戶端 axios,儲存資料的localforage,餓了麼元件 Element,所以一起執行命令安裝,後面涉及的時候可以直接使用

狀態管理器:npm install vuex --save
路由管理器:npm install vue-router
HTTP 客戶端:npm install axios
儲存 API 資料:npm install localforage
餓了餓元件:npm i element-ui -S

通過安裝前處理器,來構建簡潔和功能更豐富的元件,sass-loader 和 node-sass 用來處理 Sass(一種 CSS 擴充套件語言)

npm install sass-loader node-sass --save-dev

開啟 main.js 檔案全域性註冊元件、依賴

 // 引入 Vue 的預設值
import Vue from 'vue'
// 引入 App.vue 的預設值
import App from './App.vue'
// 引入 Router 的預設值
import router from './router'
// 引入 store/index.js 的預設值
import store from './store'
// 引入 Element 元件
import ElementUI from 'element-ui'
// 引入 Element 樣式
import 'element-ui/lib/theme-chalk/index.css'

// 設定 false 以阻止 Vue 在啟動時生成生產提示
Vue.config.productionTip = false
// 註冊 Element
Vue.use(ElementUI) 
// eslint 配置,允許 new 一個例項後不賦值,我們沒有使用 eslint,如果有,則下一行註釋不可缺少
/* eslint-disable no-new */
// 建立一個新的 Vue 例項
new Vue({
  //掛載路由
  router,
  //掛載狀態
  store,
  render: h => h(App),
}).$mount('#app')

新建路由配置 src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import beforeEach from './beforeEach'
//引入鑑權路由
import auth from '@/views/auth/routes' 
import home from '@/views/home/routes'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    ...auth,...home,
  ]
})

//鉤子函式
router.beforeEach(beforeEach) 

export default router

在路由跳轉的時候,我們需要一些許可權判斷或者其他操作。這個時候就需要使用路由的鉤子函式。
src/router/beforeEach.js

import localforage from 'localforage'
import store from  '@/store'

const beforeEach = (to, from, next) => {
  let loginRoute = 'authLogin'
  if (to.name === loginRoute) { //臨時去除登陸判斷
    localforage.getItem('token').then( token => {
      if (!token || !token.hasOwnProperty('access_token') || ((new Date().getTime() - token.created_at) / 1000) >= token.expires_in) {
        next({name: loginRoute})
      } else {
        if (!store.getters.token) {
          store.commit('SET_TOKEN', {token})
        }
        next()
      }
    })
  } else {
    next()
  }
}

export default beforeEach 

建立頁頭元件src/components/AppHeader.vue

<template>
  <el-header>
    <el-row>
      <el-col :span="18">
        Logo
      </el-col>
      <el-col :span="6">
        <div class="right">
          <el-button type="text">登陸</el-button>
          <el-button type="text">註冊</el-button>
          <el-dropdown>
            <el-button :plain="true">
              <img src="" width="30" height="30" style="border-radius:30px">
              <i class="el-icon-arrow-down el-icon--right"></i>
            </el-button>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item @click.native="logout">Logout</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </div>
      </el-col>
    </el-row>
  </el-header>
</template>

<script>
  export default {
    name: 'AppHeader',
    data() {
      return {
      }
    },
    methods: {
      logout ()  {
      },
    },
    computed: {
    }
  }
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .el-header {
    border-bottom: 1px solid #e6e6e6;
    height: 60px;
    line-height:60px;
  }
  .el-button {
    border:none;
  }
  .right {
    float:right;
  }
  a {
    text-decoration:none;
  }
</style> 

使用頁頭元件src/App.vue

<template>
  <div id="app">
    <el-container direction="vertical">
      <app-header></app-header>
      <el-main>
        <!-- 引入路由 -->
        <router-view />
      </el-main>
    </el-container>
  </div>
</template>

<script>
import  AppHeader  from "./components/AppHeader"
export default {
  name: 'app',
  components: {
    AppHeader,
  }
}
</script>

<style>
body {
  margin: 0;
}
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
</style>

建立狀態管理src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import auth from './modules/auth'
import plugin from './plugin'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    auth,
  },
  plugins: [plugin]
})

export default store

新建src/store/plugin.js

import { setHttpToken } from '../utils/http'

const subscribe = (store) => {
  store.subscribe((mutation, state) => {
    switch (mutation.type) {
      case 'SET_TOKEN':
        setHttpToken(state.auth.token.access_token)
    }
  })
}

export default (store) => {
  subscribe(store)
};

新建src/utils/http/index.js

import axios  from 'axios'
import { Message } from 'element-ui'
import router from '../../router'

const httpRequest = axios.create({
  timeout: 10000,
  baseURL: process.env.VUE_APP_API_URL
})

httpRequest.interceptors.request.use(
  config => {
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

export function setHttpToken(token) {
  httpRequest.defaults.headers.common.Authorization = `Bearer ${token}`
}

httpRequest.interceptors.response.use(
  response => {
    return response
  },
  error => {
    let message = error.response.data.message ? error.response.data.message : error.response.statusText
    let dangerouslyUseHTMLString = false

    if (error.response.status === 422 && error.response.data.hasOwnProperty('errors')) {
      message += '<br>';
      for (let key in error.response.data.errors) {
        let items = error.response.data.errors[key]
        if (typeof items === 'string') {
          message += `${items} <br>`
        } else {
          error.response.data.errors[key].forEach( item => {
            message += `${item} <br>`
          })
        }
      }
      dangerouslyUseHTMLString = true
    }

    if (error.response.status === 401 && error.response.data.message === 'Unauthenticated.') {
      router.push({name: 'authLogin'})
    }

    Message({
      dangerouslyUseHTMLString,
      message: message,
      type: 'error'
    })

    return Promise.reject(error)
  }
)

export default httpRequest

新建登陸頁面和對應的路由src/views/auth/login.vue

<template>
  <el-card>
    <el-form ref="form" :model="form" label-width="80px">
      <el-form-item label="使用者名稱">
        <el-input v-model="form.username"></el-input>
      </el-form-item>
      <el-form-item label="密碼">
        <el-input v-model="form.password" type="password"></el-input>
      </el-form-item>
      <el-form-item label="型別">
        <el-radio v-model="form.provider"  label="student">學生</el-radio>
        <el-radio v-model="form.provider" label="teacher">教師</el-radio>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">登陸</el-button>
        <el-button type="primary">Line登陸</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script>
  import { login } from "../../api/login"
  export default {
    name: "login",
    components: {
    },
    data() {
      return {
        form: {
          username:"",
          password:"",
          provider: "student",
        }
      }
    },
    methods: {
      onSubmit() {
        login({username: this.form.username, password: this.form.password, provider: this.form.password}).then(response => {
          console.log(response)
        })
      }
    }
  }
</script>

<style scoped>
</style> 

src/views/auth/routes.js

 export default [
  {
    path: '/auth/login',
    name: 'login',
    component: () => import('./login')
  }
]

新建首頁和對應的路由src/views/home/home.vue

<template>
  <div>
    <el-container>
      <el-header>
        <el-menu
                :default-active="activeIndex2"
                class="el-menu-demo"
                mode="horizontal"
                @select="handleSelect"
                background-color="#545c64"
                text-color="#fff"
                active-text-color="#ffd04b">
          <el-menu-item index="1">處理中心</el-menu-item>
          <el-submenu index="2">
            <template slot="title">我的工作臺</template>
            <el-menu-item index="2-1">選項1</el-menu-item>
            <el-menu-item index="2-2">選項2</el-menu-item>
            <el-menu-item index="2-3">選項3</el-menu-item>
            <el-submenu index="2-4">
              <template slot="title">選項4</template>
              <el-menu-item index="2-4-1">選項1</el-menu-item>
              <el-menu-item index="2-4-2">選項2</el-menu-item>
              <el-menu-item index="2-4-3">選項3</el-menu-item>
            </el-submenu>
          </el-submenu>
          <el-menu-item index="3" disabled>訊息中心</el-menu-item>
          <el-menu-item index="4"><a href="https://www.ele.me" target="_blank">訂單管理</a></el-menu-item>
        </el-menu>
      </el-header>
      <el-main>Main</el-main>
    </el-container>

  </div>
</template>

<script>
  export default {
    name: "home",
    data() {
      return {
        activeIndex: '1',
        activeIndex2: '1'
      };
    },
    methods: {
      /*handleSelect(key, keyPath) {
        console.log(key, keyPath)
      }*/
    }
  }
</script>

<style scoped>
</style> 

src/views/home/routes.js

 export default [
  {
    path: '/',
    name: 'home',
    component: () => import('./home')
  }
]

新建src/api/login.js

import http from '@/utils/http'

export const login = ({ username, password, provider }) => {
  return http.post('/oauth/token', {
    username,
    password,
    provider,
    grant_type: 'password',
    client_id: process.env.VUE_APP_AUTH_CLIENT_ID,
    client_secret: process.env.VUE_APP_AUTH_CLIENT_SECRET
  })
} 

最終顯示了一個比較醜陋的原始頁面,接下來,我們需要一步一步完善它。
Vue.js+Laravel 前後端分離的多表多使用者登陸課題(三)

本作品採用《CC 協議》,轉載必須註明作者和本文連結

一直在學習,從未停止,終身學習。

相關文章