※ 2018/10/17 Laravel5.6 から 5.7 に記述を更新
Laravel でユーザ認証した後、Vue側でRouter使ってページを切り替えてみる
Vuetify のデータテーブルを使ったユーザ管理ページを作って、データテーブルの検索やページング、ソートの機能も使ってみる
環境設定他関連記事はこちら
Laravel + Vue + Vuetify で業務サイト作ってみる
1.各パッケージをインストール
npm でサクッと vue-router と vuetify を入れとく
vuetify はこちらを参考に
npm install vue-router
npm install vuetify
npm install css-loader
npm install material-design-icons-iconfont
インストール後はこんな感じ
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.18",
"bootstrap": "^4.0.0",
"cross-env": "^5.1",
"jquery": "^3.2",
"laravel-mix": "^2.0",
"lodash": "^4.17.5",
"popper.js": "^1.12",
"vue": "^2.5.7"
},
"dependencies": {
"css-loader": "^1.0.0",
"material-design-icons-iconfont": "^3.0.3",
"vue-router": "^3.0.1",
"vuetify": "^1.2.9"
}
}
Vuetify は 1.2.9 が入りました
2.読み込み設定
追加した Vuetify と router を読み込むように設定
router については外出しして管理しやすいようにしとく
Vuetify は色設定 https://vuetifyjs.com/ja/style/theme も入れておく
require('./bootstrap');
// Vue
import Vue from 'vue'
// Vuetify
import Vuetify from 'vuetify'
import colors from 'vuetify/es5/util/colors'
Vue.use(Vuetify, {
theme: {
primary: colors.indigo.base,
secondary: colors.blue.base,
accent: colors.amber.base,
}
});
import 'vuetify/dist/vuetify.min.css'
import 'material-design-icons-iconfont/dist/material-design-icons.css'
// Vue-Router
import router from './router'
// Main app
const app = new Vue({
el: '#app',
router,
});
router 用設定
app.js と同じ階層に router ディレクトリを掘っておく
$ mkdir resources/js/router
作った ディレクトリに router 用設定ファイルを作成
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
//
import example_component from '../components/ExampleComponent.vue'
import admin_component from '../components/AdminComponent.vue'
import r_link from '../components/RouterLink.vue'
//
Vue.component('example-component', example_component)
Vue.component('admin-component', admin_component)
Vue.component('r-link', r_link)
//
import home from '../components/HomeComponent.vue'
import admin_user from '../components/Admin/UserComponent.vue'
export default new Router({
mode: 'history',
routes: [
{ path: '/', name: 'home', component: home, meta: {name: 'ホーム', icon: 'home'}},
{ path: '/admin/user', name: 'admin_user', component: admin_user, meta: {name: '社員管理', icon: 'supervisor_account'}},
],
})
リンク用の文字とかアイコンをここに定義
各画面で表示する文字とかアイコンはこの定義を参照することで変更も1か所で済むようにしておく
3.routerで切り替えるベースコンポーネント作成
vue-routerで切り替えるページのベースとなるコンポーネントを作成する
レイアウトは Vuetify のテンプレートを利用させてもらう
<template>
<v-app id="app">
<v-navigation-drawer v-model="drawer" clipped fixed app >
<v-list dense>
<r-link linkname='home'></r-link>
<r-link linkname='admin_user'></r-link>
</v-list>
</v-navigation-drawer>
<v-toolbar color="primary" dark fixed app clipped-left>
<v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
<v-toolbar-title>{{title}}</v-toolbar-title>
<v-spacer></v-spacer>
{{ name }}
<v-btn icon @click="axiosLogout()">
<v-tooltip left>
<v-icon slot="activator" color="white" dark >exit_to_app</v-icon>
<span>ログアウト</span>
</v-tooltip>
</v-btn>
</v-toolbar>
<v-content>
<v-container fluid fill-height
<v-layout justify-center fluid column>
<v-fade-transition mode="out-in">
<router-view @axios-logout="axiosLogout"></router-view>
</v-fade-transition>
</v-layout>
</v-container>
</v-content>
<v-footer color="primary" dark app fixed>
<span class="white--text ml-3" v-html="footer"></span>
</v-footer>
</v-app>
</template>
<script>
export default {
name: 'AdminComponent',
props: {
name: String,
logout: String,
},
data: () => ({
drawer: false,
footer: 'foo-----footer',
title: 'tit------title',
}),
mounted() {
console.log('AdminComponent mounted.')
if (process.env.MIX_FOOTER) { this.footer = process.env.MIX_FOOTER }
if (process.env.MIX_TITLE) { this.title = process.env.MIX_TITLE }
},
methods: {
axiosLogout: function() {
axios.post(this.logout)
.then( function (response) {
console.log(response)
}.bind(this))
.catch(function (error) {
console.log(error)
if (error.response) {
if (error.response.status) {
if (error.response.status == 401 || error.response.status == 419) {
var parser = new URL(this.logout)
location.href=parser.origin
}
}
}
}.bind(this))
},
},
}
</script>
リンク部分をコンポーネント化
リンク毎に同じ定義を書くのもめんどくさいのでコンポーネントにして、アイコンやらタイトルやらは route/index.js の定義から設定できるようにしとく
<template>
<router-link :to="{name: linkname}">
<v-list-tile>
<v-list-tile-action>
<v-icon>{{ getRoute( linkname, 'icon' ) }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>{{getRoute( linkname, 'name' )}}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</router-link>
</template>
<script>
export default {
name: 'routerlink',
props: {
linkname: String,
},
methods: {
getRoute(name, key) {
for(var i=0 ; i<this.$router.options.routes.length; i++) {
if (this.$router.options.routes[i].name == name ) {
return this.$router.options.routes[i].meta[key]
}
}
},
},
}
</script>
<style>
a:hover {
text-decoration: none;
}
</style>
4.home コンポーネント作成
とりあえず空のコンポーネント
Vuetifyで書いておく
<template>
<v-content>
<v-container fluid fill-height>
<v-layout justify-center fluid>
<v-flex xs12 offset-mx5>
Home Component.
</v-flex>
</v-layout>
</v-container>
</v-content>
</template>
<script>
export default {
data: () => ({
}),
props: {
},
created() {
console.log('Home Component created.')
this.initialize()
},
mounted() {
console.log('Home Component mounted.')
},
methods: {
initialize: function() {
},
},
}
</script>
5.ユーザ一覧を表示するコンポーネントを作成
Vuetify のデータテーブルを利用して登録済みのユーザ(社員)情報を一覧表示してみます
ユーザ情報は axios で取ってきてデータテーブルにセット
検索とページングとソートの設定もしときます
データテーブル構造は動的に headers に設定しているやつをセットするようにしてます
ついでに連番も表示するようにしておきました
<template>
<v-flex>
<v-card xs12 class="m-3 px-3">
<v-card-title class="title">
<v-icon class="pr-2">{{ $route.meta.icon }}</v-icon> {{ $route.meta.name }} {{ /* 社員管理 */ }}
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="tabledata"
:pagination.sync="pagination"
:rows-per-page-items='[10,25,50,{"text":"All","value":-1}]'
:loading="loading"
:search="search"
class="elevation-0 p-1"
>
<v-progress-linear slot="progress" color="blue" indeterminate></v-progress-linear>
<template slot="items" slot-scope="props">
<tr>
<td class="text-xs-center" xs1>{{ (props.index + 1) + (pagination.page - 1) * pagination.rowsPerPage }}</td>
<template v-for="n in (headers.length - 1)">
<td :class="'text-xs-' + headers[n].align" style="white-space: nowrap;" v-text="props.item[headers[n].value]"></td>
</template>
</tr>
</template>
</v-data-table>
</v-card>
</v-flex>
</template>
<script>
export default {
name: 'UserComponent',
props: {
logout: String,
},
data: () => ({
loading: true,
search: '',
pagination: { sortBy: 'name', descending: false, },
tabledata: [],
headers: [
{ align: 'center', sortable: false, text: 'No', },
{ align: 'left', sortable: true, text: '社員ID', value: 'loginid' },
{ align: 'left', sortable: true, text: '氏名', value: 'name' },
{ align: 'center', sortable: true, text: '権限', value: 'role' },
],
}),
created() {
console.log('User Component created.')
this.initialize()
},
methods: {
initialize: function() {
this.getUsers()
},
getUsers() {
this.loading = true
axios.post('/api/admin/user')
.then( function (response) {
this.loading = false
console.log(response)
if (response.data.users) {
this.tabledata = response.data.users
this.setRole()
}
}.bind(this))
.catch(function (error) {
this.loading = false
console.log(error)
if (error.response) {
if (error.response.status) {
if (error.response.status == 401 || error.response.status == 419) {
this.$emit('axios-logout')
}
}
}
}.bind(this))
},
setRole() {
for (var i=0; i<this.tabledata.length; i++) {
if (this.tabledata[i].role) {
if (this.tabledata[i].role == 5) { this.tabledata[i].role = '管理者' }
if (this.tabledata[i].role == 10) { this.tabledata[i].role = 'ユーザ' }
}
}
},
},
}
</script>
権限(role)は数字で出されてもわかりにくいので日本語に変換(setRole)して表示するようにしています
6.Laravel側でユーザ用コントローラを作成
ひな形をまず作って
php artisan make:controller UserController
一覧のデータとして全件を返す index を定義しときます
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
class UserController extends Controller
{
public function index()
{
$users = User::all();
return ['users' => $users];
}
}
権限(role)も表示対象にする
App/User.php でroleは隠す設定にしていたので、hiddenからfillableに変更
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
// 'email',
'loginid',
'password',
'role',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
}
7.Laravel側のルーティング設定
管理者以外はアクセスできないように定義したGATEで制限(can:admin)もかけときます
<?php
Route::get('/', function () {
return view('home');
})->middleware('auth');
// Authentication Routes...
Route::get('/login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('/login', 'Auth\LoginController@login');
Route::post('/logout', 'Auth\LoginController@logout')->name('logout');
// Admin
Route::group( ['middleware' => ['auth', 'can:admin']], function() {
// USER
Route::post('/api/admin/user', 'UserController@index')->name('admin/user');
});
// Other
Route::get('/{any}', function () {
return view('home');
})->middleware('auth')->where('any', '.*');
8.動作確認
コンパイルして、サーバを起動して
npm run dev
php artisan serve --host=172.16.0.100 --port=8000
ブラウザでアクセスして
http://172.16.0.100:8000/
↓↓
ログインページへ飛ばされて
http://172.16.0.100:8000/login
↓↓
User の権限でログインしたら今までの Vue コンポーネントが表示されて
↓↓
ログアウトして 今度は Admin の管理者権限でログインしたら
↓↓
Vuetify で作った Adminコンポーネントが表示されて
↓↓
メニューから 「社員管理」を選ぶと vue-router の機能で一覧のコンポーネントに切り替わり
↓↓
User情報を axios が取ってきて Vuetify のデータテーブルにセットして表示
検索とかページングとかソートとかの動作もうまく動いているようです
9. ヘッダーとフッターの環境設定
ヘッダーとフッターを環境変数から取得するようにしてあるので
~~
mounted() {
console.log('AdminComponent mounted.')
if (process.env.MIX_FOOTER) { this.footer = process.env.MIX_FOOTER }
if (process.env.MIX_TITLE) { this.title = process.env.MIX_TITLE }
},
~~
環境変数を設定
~~
MIX_FOOTER="フッター文字列を指定"
MIX_TITLE="タイトル指定"
末尾に定義を追加
ついでにVuetifyの色設定も変更してみる
~~~
Vue.use(Vuetify, {
theme: {
primary: colors.amber.base,
~~~
indigo 指定を amber 指定に変更
コンパイルして再表示
npm run dev
php artisan serve --host=172.16.0.100 --port=8000
以上
Vue-Router でページを切り替えて
Vuetify でデータテーブルを表示できました