Laravel+vue.js 前后端分离解决跨域后获取数据库用户列表(五)
接续于 = >《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 协议》,转载必须注明作者和本文链接
推荐文章: