接續於 = >《Vue.js+Laravel 前後端分離 API 多使用者多表登陸課題(四)》
安裝第三方擴充套件spatie/laravel-cors,花一分鐘時間配置,就可以解決跨域問題。具體簡單的操作可以前往大神的Github閱讀文件。
下面開始講述獲取學生和老師的資料庫的使用者列表,並顯示在前端介面上。首先完善後端Laravel的程式碼,因為學生和老師的程式碼是一樣的,下面以學生的程式碼為例子進行講述。
建立學生資源類
php artisan make:resource StudentResource
對生成的檔案app/Http/Resources/StudentResource.php進行修改
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class StudentResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => (string)$this->created_at,
'updated_at' => (string)$this->updated_at,
];
}
}
建立學生控制器
php artisan make:controller Home/StudentController
對生成的檔案app/Http/Controllers/Home/StudentController.php進行修改
<?php
namespace App\Http\Controllers\Home;
use App\Http\Resources\StudentResource;
use App\Models\Student;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class StudentController extends Controller
{
public function index()
{
$students = Student::all();
return StudentResource::collection($students);
}
}
修改路由routes/api.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::namespace('Home')->group(function () {
Route::get("student", "StudentController@index");
Route::get("teacher", "TeacherController@index");
});
建立 database/seeds/StudentsAndTeacherSeeder.php 檔案
php artisan make:seed StudentsAndTeacherSeeder
新增測試資料
<?php
use App\Models\Student;
use App\Models\Teacher;
use Illuminate\Database\Seeder;
class StudentsAndTeacherSeeder extends Seeder {
/**
* Run the database seeds.
*
* @return void
*/
public function run() {
Student::query()->truncate();
Student::create([
"name" => "student1",
"email" => "student1@gmail.com",
'password' => bcrypt('student1'),
]);
Student::create([
"name" => "student2",
"email" => "student2@gmail.com",
'password' => bcrypt('student2'),
]);
Student::create([
"name" => "student3",
"email" => "student3@gmail.com",
'password' => bcrypt('student3'),
]);
Student::create([
"name" => "student4",
"email" => "student4@gmail.com",
'password' => bcrypt('student4'),
]);
Student::create([
"name" => "student5",
"email" => "student5@gmail.com",
'password' => bcrypt('student5'),
]);
Teacher::query()->truncate();
Teacher::create([
"name" => "teacher1",
"email" => "teacher1@gmail.com",
'password' => bcrypt('teacher1'),
]);
Teacher::create([
"name" => "teacher2",
"email" => "teacher2@gmail.com",
'password' => bcrypt('teacher2'),
]);
Teacher::create([
"name" => "teacher3",
"email" => "teacher3@gmail.com",
'password' => bcrypt('teacher3'),
]);
Teacher::create([
"name" => "teacher4",
"email" => "teacher4@gmail.com",
'password' => bcrypt('teacher4'),
]);
Teacher::create([
"name" => "teacher5",
"email" => "teacher5@gmail.com",
'password' => bcrypt('teacher5'),
]);
}
}
匯入資料庫
php artisan db:seed --class=StudentsAndTeacherSeeder
使用Postman測試Api,成功獲取相應的資料
至此後端程式碼開發完畢,接下來講述前端Vue.js的部署
前端部分,新建環境變數檔案.env.local
VUE_APP_API_URL=http://mytestdomain.test/
VUE_APP_AUTH_CLIENT_ID=2
VUE_APP_AUTH_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxx
對axios封裝一個src/utils/http/index.js
import axios from 'axios' // 基於promise的http庫,攔截請求和響應、取消請求、轉換json、客戶端防禦cSRF等
import { Message } from 'element-ui' // 常用於主動操作後的反饋提示,用於系統級通知的被動提醒。
import router from '../../router' // 引入路由
// 自定義 axios 例項新增攔截器,傳送請求前可以進行一個請求的攔截,檢測是否符合相應的許可權操作
const httpRequest = axios.create({
timeout: 10000, //設定預設的請求超時時間。例如超過了10s,就會告知使用者當前請求超時,請重新整理等。
baseURL: process.env.VUE_APP_API_URL //設定請求地址
})
// 新增請求攔截器
httpRequest.interceptors.request.use(
// 在傳送請求之前做些什麼
// 每次傳送請求之前判斷vuex中是否存在token
// 如果存在,則統一在http請求的header都加上token,這樣後臺根據token判斷你的登入情況
// 即使本地存在token,也有可能token是過期的,所以在響應攔截器中要對返回狀態進行判斷
config => {
return config
},
// 對請求錯誤做些什麼
error => {
return Promise.reject(error)
}
)
// 匯出帶有token的 HTTP 請求頭
export function setHttpToken(token) {
httpRequest.defaults.headers.common.Authorization = `Bearer ${token}`
}
// 新增響應攔截器
httpRequest.interceptors.response.use(
// 對響應資料做點什麼
// 如果返回的狀態碼為200,說明介面請求成功,可以正常拿到資料
// 否則的話丟擲錯誤
response => {
return response
},
// 對響應錯誤做點什麼
// 伺服器狀態碼不是2開頭的的情況
// 這裡可以跟你們的後臺開發人員協商好統一的錯誤狀態碼
// 然後根據返回的狀態碼進行一些操作,例如登入過期提示,錯誤提示等等
error => {
let message = error.response.data.message ? error.response.data.message : error.response.statusText
let dangerouslyUseHTMLString = false
// 錯誤程式碼 422
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
}
// 錯誤程式碼 401
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
新建一個登陸Api檔案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
})
}
對localforage封裝一個工具src/utils/auth/index.js
import localforage from 'localforage'
const TOKEN = 'token';
// 當值被儲存後
export const setToken = (token) => {
return localforage.setItem(TOKEN, token)
}
// 當離線倉庫中的值被載入時
export const getToken = () => {
return localforage.getItem(TOKEN)
}
// 當值被移除後
export const removeToken = () => {
return localforage.removeItem(TOKEN)
}
新建src/store/modules/auth.js
import { login } from '../../api/login'
import { removeToken, setToken } from '../../utils/auth'
const state = {
token: '',
}
const getters = {
//token
token: state => state.token,
//授權token
accessToken : state => state.token.access_token,
//授權的型別
provider: state => state.token.provider,
//判斷是否登入
isLogin: state => state.token.created_at + state.token.expires_in * 1000 > new Date().getTime(),
}
const mutations = {
SET_TOKEN (state, {token}) {
state.token = token
}
}
const actions = {
//登入
loginHandle ({ commit }, { username, password, provider }) {
return new Promise((resolve, reject) => {
return login({username, password, provider})
.then(response => {
const token = {
...response.data,
created_at: new Date().getTime(),
provider
}
commit('SET_TOKEN', {token})
resolve(setToken(token))
})
.catch(error => {
reject(error)
})
})
},
logoutHandle ({ commit }) {
return new Promise(() => {
removeToken()
commit("SET_TOKEN", { token: {} })
})
}
}
export default {
state, // state 類似與元件中的 data 資料
getters, // getter 存放公共函式供元件呼叫,類似於元件中的過濾函式 computed 或者 filters
mutations, // mutations 類似於元件裡面的 methods 在 mutations 裡面可以對 state 的資料進行修改
actions // actions 類似於mutations 大體歸類到事件,mutation像事件註冊,需要相應的條件觸發,action像是管理觸發條件。
}
新建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/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import auth from './modules/auth'
import plugin from './plugin'
Vue.use(Vuex)
// 建立 store 物件
const store = new Vuex.Store({
modules: {
auth,
},
plugins: [plugin]
})
// 匯出 store 物件
export default store
新建獲取學生資料的src/api/student.js
import http from '../utils/http'
// 獲取全部學生資料
export const allStudent = () => {
return http.get('/api/student')
}
// 獲取學生資料
export const me = () => {
return http.get('/api/student-me')
}
// 獲取學生關注老師的資料
export const teachers = () => {
return http.get("/api/subscribe-teachers")
}
修改前端現實的頁面src/views/home/home.vue
<template>
<div>
<el-tabs type="border-card">
<el-tab-pane label="老師列表">
<el-table
:data="teachers"
style="width: 100%">
<el-table-column
prop="name"
label="老師名稱">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="200">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">關注</el-button>
<el-button type="text" size="small">取消關注</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="學生列表">
<el-table
:data="students"
style="width: 100%">
<el-table-column
prop="name"
label="老師名稱">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="200">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">關注</el-button>
<el-button type="text" size="small">取消關注</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { allTeacher } from "../../api/teacher"
import { allStudent } from "../../api/student"
export default {
name: "home",
data() {
return {
students: [],
teachers: []
}
},
methods: {
/*handleSelect(key, keyPath) {
console.log(key, keyPath)
}*/
},
created() { //資料初始化
allTeacher().then(response => {
this.teachers = response.data.data
})
allStudent().then(response => {
this.students = response.data.data
})
}
}
</script>
<style scoped>
</style>
即可獲得後臺傳進來的資料,顯示如下
本作品採用《CC 協議》,轉載必須註明作者和本文連結