Vue.js+Laravel 前后端分离的多表多用户登陆课题(三)

接续于 = >《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 协议》,转载必须注明作者和本文链接
一直在学习,从未停止,终身学习。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

搞得不错,继续加油哦!

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!