接續於 = >《Laravel 多使用者多表登陸的第三方擴充套件包 passport-multiauth(二)》
Vue CLI 建立新專案 multiauthvue
vue create multiauthvue
npm run serve 後可見如下圖歡迎頁面
因為已經確定了要使用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
})
}
最終顯示了一個比較醜陋的原始頁面,接下來,我們需要一步一步完善它。
本作品採用《CC 協議》,轉載必須註明作者和本文連結