Laravel Vuejs 实战:开发知乎 (42-44)用户头像
1.组件式上传头像vue组件:
安装扩展包:vue-image-crop-upload 或者 Vue Avatar Cropper 【我用的这个,暂时弃坑,需要加一个csrftoken】
执行:1 npm i vue-avatar-cropper1 npm install & npm run watch-poll
执行:
1 npm install vue-image-crop-upload
1 npm run watch-poll
2.路由:
添加:
1 #region 2 //访问用户详细页面 3 4 Route::get('/avatar', 'UserController@avatar')->name('users.avatar'); 5 6 #endregion 7
3.执行:
1 php artisan make:controller UserController
增加UserContoller.php,代码如下:
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use Illuminate\Http\Request; 6 7 class UserController extends Controller 8 { 9 // 10 11 /** 返回用户avatar页面视图 12 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 13 */ 14 public function avatar() 15 { 16 return view('users._avatar'); 17 } 18 19 20 } 21 22
4.增加视图文件:\views\users\_avatar.blade.php:
1 @extends('layouts.app') 2 3 @section('content') 4 5 <div class="container"> 6 <div class="row"> 7 <div class="col-md-8 col-md-offset-2"> 8 <div class="card"> 9 <div class="card-header"> 10 更换头像 11 </div> 12 <div class="card-body"> 13 <avatar avatar="{{ auth()->user()->avatar }}"></avatar> 14 </div> 15 </div> 16 </div> 17 </div> 18 </div> 19 20 @endsection 21 22
5.vue组件:
执行:
1 npm install babel-polyfill
代码:
1 <template> 2 <div> 3 <a class="btn btn-success" @click="toggleShow">设置头像</a> 4 <my-upload field="img" 5 @crop-success="cropSuccess" 6 @crop-upload-success="cropUploadSuccess" 7 @crop-upload-fail="cropUploadFail" 8 v-model="show" 9 :width="300" 10 :height="300" 11 url="/upload" 12 :params="params" 13 :headers="headers" 14 img-format="png"></my-upload> 15 <img :src="imgDataUrl"> 16 </div> 17 </template> 18 19 <script> 20 import 'babel-polyfill'; // es6 shim 21 import myUpload from 'vue-image-crop-upload'; 22 23 export default { 24 props: ['avatar'], 25 name: "Avatar", 26 data: function () { 27 return { 28 show: false, 29 params: { 30 token: '123456798', 31 name: 'avatar' 32 }, 33 headers: { 34 smail: '*_~' 35 }, 36 imgDataUrl: this.avatar // the datebase64 url of created image 37 } 38 }, 39 components: { 40 'my-upload': myUpload 41 }, 42 methods: { 43 toggleShow() { 44 this.show = !this.show; 45 }, 46 /** 47 * crop success 48 * 49 * [param] imgDataUrl 50 * [param] field 51 */ 52 cropSuccess(imgDataUrl, field) { 53 console.log('-------- 剪截成功 --------'); 54 this.imgDataUrl = imgDataUrl; 55 }, 56 /** 57 * upload success 58 * 59 * [param] jsonData server api return data, already json encode 60 * [param] field 61 */ 62 cropUploadSuccess(jsonData, field) { 63 console.log('-------- 上传成功 --------'); 64 console.log(jsonData); 65 console.log('field: ' + field); 66 }, 67 /** 68 * upload fail 69 * 70 * [param] status server api return error status, like 500 71 * [param] field 72 */ 73 cropUploadFail(status, field) { 74 console.log('-------- 上传失败 --------'); 75 console.log(status); 76 console.log('field: ' + field); 77 } 78 } 79 80 } 81 </script> 82 83 <style scoped> 84 85 </style> 86 87
6.app.js:
1 /** 2 * First we will load all of this project's JavaScript dependencies which 3 * includes Vue and other libraries. It is a great starting point when 4 * building robust, powerful web applications using Vue and Laravel. 5 */ 6 7 require('./bootstrap'); 8 require('../../vendor/select2/select2/dist/js/select2.js'); 9 // 将views/vendor/ueditor/assets.blade.php中的引用换到本处 10 require('../../public/vendor/ueditor/ueditor.config.js'); 11 require('../../public/vendor/ueditor/ueditor.all.js'); 12 13 window.Vue = require('vue'); 14 15 /** 16 * The following block of code may be used to automatically register your 17 * Vue components. It will recursively scan this directory for the Vue 18 * components and automatically register them with their "basename". 19 * 20 * Eg. ./components/ExampleComponent.vue -> <example-component></example-component> 21 */ 22 23 // const files = require.context('./', true, /\.vue$/i) 24 // files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default)) 25 26 // Vue.component('example-component', require('./components/ExampleComponent.vue').default); 27 Vue.component('question-follow-button', require('./components/QuestionFollowButton').default); 28 Vue.component('user-follow-button', require('./components/UserFollowButton').default); 29 Vue.component('user-vote-button', require('./components/UserVoteButton').default); 30 Vue.component('send-message', require('./components/SendMessage').default); 31 Vue.component('comments', require('./components/Comments').default); 32 Vue.component('avatar', require('./components/Avatar').default); 33 /** 34 * Next, we will create a fresh Vue application instance and attach it to 35 * the page. Then, you may begin adding components to this application 36 * or customize the JavaScript scaffolding to fit your unique needs. 37 */ 38 39 const app = new Vue({ 40 el: '#app', 41 }); 42 43
7.配置图片上传到服务器本地:
web.php添加:
1 Route::post('/avatar/upload', 'UserController@avatarStore')->name('users.avatarUpload');
1 <?php 2 3 /* 4 |-------------------------------------------------------------------------- 5 | Web Routes 6 |-------------------------------------------------------------------------- 7 | 8 | Here is where you can register web routes for your application. These 9 | routes are loaded by the RouteServiceProvider within a group which 10 | contains the "web" middleware group. Now create something great! 11 | 12 */ 13 14 Route::get('/', function () { 15 return view('welcome'); 16 }); 17 18 Auth::routes(['verify' => true]); 19 20 Route::get('/home', 'HomeController@index')->name('home'); 21 22 Route::resource('questions', 'QuestionController'); 23 24 25 #region 回答路由CRUD 26 27 //查看回答 以及 回答的form 都是在questions详细内容页面 28 29 //提交回答 30 Route::post('questions/{question}/answers', 'AnswerController@store')->name('answers.store'); 31 32 //更新回答 33 34 35 //删除回答 36 37 38 #endregion 39 40 41 #region 42 //用户关注 取消关注问题 43 Route::get('questions/{question}/follow', 'QuestionController@follow')->name('questions.follow'); 44 #endregion 45 46 47 #region 48 49 //用户通知消息路由 50 Route::get('/notifications', 'NotificationController@index')->name('notification.index'); 51 #endregion 52 53 #region 54 //用户查看短消息 55 Route::get('/inbox', 'InboxController@index')->name('inbox.index'); 56 57 //展示用户间私信对话具体内容页 58 Route::get('/inbox/{userId}', 'InboxController@show')->name('inbox.show'); 59 60 //用户回信息 61 Route::post('/inbox/{userId}/send', 'InboxController@store')->name('inbox.store'); 62 #endregion 63 64 65 #region 66 //访问用户详细页面 67 68 Route::get('/avatar', 'UserController@avatar')->name('users.avatar'); 69 70 Route::post('/avatar/upload', 'UserController@avatarStore'); 71 72 #endregion 73 74 75 76
UserContoller.php增加代码如下:
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use Illuminate\Http\Request; 6 7 class UserController extends Controller 8 { 9 // 10 public function __construct() 11 { 12 $this->middleware('auth'); 13 } 14 15 16 /** 返回用户avatar页面视图 17 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View 18 */ 19 public function avatar() 20 { 21 return view('users._avatar'); 22 } 23 24 public function avatarStore(Request $request) 25 { 26 $user = auth()->user(); 27 $file = $request->file('img'); 28 $fileName = md5(time() . $user->id) . '.' . $file->getClientOriginalExtension(); 29 $file->move('uploads/image/avatars', $fileName); 30 31 $user->avatar = asset('uploads/image/avatars/' . $fileName); 32 33 $user->save(); 34 35 return response()->json( 36 [ 37 'url' => $user->avatar, 38 ]); 39 } 40 } 41 42
Vue文件修改:
注意这个token在laravel中应该是_token,否则会419未授权。
1 <template> 2 <div> 3 <a class="btn btn-success" @click="toggleShow">设置头像</a> 4 <my-upload field="img" 5 @crop-success="cropSuccess" 6 @crop-upload-success="cropUploadSuccess" 7 @crop-upload-fail="cropUploadFail" 8 v-model="show" 9 :width="300" 10 :height="300" 11 url="/avatar/upload" 12 :params="params" 13 :headers="headers" 14 img-format="png"></my-upload> 15 <img :src="imgDataUrl" style="width:50px"> 16 </div> 17 </template> 18 19 <script> 20 import 'babel-polyfill'; // es6 shim 21 import myUpload from 'vue-image-crop-upload'; 22 23 export default { 24 props: ['avatar'], 25 name: "Avatar", 26 data: function () { 27 return { 28 show: false, 29 params: { 30 _token: document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 31 name: 'avatar' 32 }, 33 headers: { 34 smail: '*_~', 35 }, 36 imgDataUrl: this.avatar // the datebase64 url of created image 37 } 38 }, 39 components: { 40 'my-upload': myUpload 41 }, 42 methods: { 43 toggleShow() { 44 this.show = !this.show; 45 }, 46 /** 47 * crop success 48 * 49 * [param] imgDataUrl 50 * [param] field 51 */ 52 cropSuccess(imgDataUrl, field) { 53 console.log('-------- 剪截成功 --------'); 54 this.imgDataUrl = imgDataUrl; 55 }, 56 /** 57 * upload success 58 * 59 * [param] jsonData server api return data, already json encode 60 * [param] field 61 */ 62 cropUploadSuccess(jsonData, field) { 63 console.log('-------- 上传成功 --------'); 64 // console.log(jsonData); 65 // console.log('field: ' + field); 66 this.toggleShow(); 67 }, 68 /** 69 * upload fail 70 * 71 * [param] status server api return error status, like 500 72 * [param] field 73 */ 74 cropUploadFail(status, field) { 75 console.log('-------- 上传失败 --------'); 76 console.log(status); 77 console.log('field: ' + field); 78 } 79 } 80 81 } 82 </script> 83 84 <style scoped> 85 86 </style> 87 88
8.七牛云存储:
略