增加使用者體驗 - 讀取中效果製作 製作局部和全部的loading效果。 step1.
全域loading效果 - Vue Loading Overlay 在這邊我們會直接安裝Vue Loading Overlay套件,來達成全域的loading效果。 輸入指令 npm install vue-loading-overlay
,來為你的編譯器安裝這個套件。
step2. 在main.js檔案中,加入overlay的套件,也要加入啟用的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---main.js--- import Vue from 'vue' import axios from 'axios' import VueAxios from 'vue-axios' import Loading from 'vue-loading-overlay'; // 引入overlay元件 import 'vue-loading-overlay/dist/vue-loading.css'; // 引入overlay元件 import 'bootstrap' import App from './App' import router from './router' Vue.use(VueAxios, axios) Vue.config.productionTip = false Vue.component('Loading', Loading); // 將overlay元件宣告成全域的元件 axios.defaults.withCredentials = true; /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
step3. 接著,在Products.vue元件檔檔案中,加入overlay的套件引入的程式碼。
1 2 3 4 5 6 7 8 9 ---Products.vue--- <template> <div> <div class="vld-parent"> <loading :active.sync="isLoading"></loading> </div> ---以下內容筆記省略不寫--- </div> </template>
你可以看到,我們在Products元件檔中,加入了一段程式碼,就是它官方提供說要加到元件的template
中的。 那這邊的話,我們只有留下<loading>
標籤中的active.sync
部分。 你可以看到,它有一個isLoading
的值,當這個值是true
的時候,會執行loading特效,若是false
的話,則不會顯現。
step4.
1 2 3 4 5 6 7 8 9 10 11 12 methods:{ getProducts(){ const vm = this; const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products/all`; vm.isLoading = true; // 啟用loading效果 this.$http.get(api).then((response) => { console.log(response.data); vm.isLoading = false; // 關閉loading效果 vm.products = response.data.products; }) } }
你可以看到,我們在Products.vue元件檔中,在getProducts中,當一進去的時候,啟用loading的動畫, 當數據接收結束之後,就將isLoading 設為 false
,關閉loading的動畫。
局部loading效果 - fontawesome step1. 我們先將fontawesome的css的cdn引入到最外層的index.html檔案中。 step2. 你可以先去fontAwesome的網站上找到那些動態loading的圖 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---Products.vue元件檔--- <template> <label for="customFile">或 上傳圖片 <i class="fas fa-spinner fa-spin" v-if="Status.isUploading"></i> </label> </template> <script> data(){ return { products:[], tempProduct:{}, isNew: false, isLoading:false, Status:{ isUploading:false } } }, </script>
你可以看到,我們先新增了Status屬性,它含有isUploading的屬性。 並在template
中那些font-awesome的loaing的圖示中,加上v-if
判斷若isUploading是true
的話就出現, 若是false
就不出現。
step3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 methods:{ uploadFile(){ console.log(this); const uploadFile = this.$refs.files.files[0]; const vm = this; const formData = new FormData(); vm.Status.isUploading = true; // 加入fontawesome的loading效果 formData.append('file-to-upload', uploadFile); const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/upload`; this.$http.post(url, formData, { headers:{ 'Content-Type': 'multipart/form-data' } }).then((response)=>{ console.log(response.data); vm.Status.isUploading = false; // 結束fontawesome的loading 效果 if(response.data.success){ vm.$set(vm.tempProduct, 'imageUrl', response.data.imageUrl) } }) } }
我們在uploadFile方法中,加入了切換Status.isUploading 的旗標,藉此來看開啟或關閉fontawesome的動畫效果。
增加使用者體驗 - 錯誤的訊息回饋 - Event Bus 一般如果用傳統的方式,當某個底層元件發生錯誤,像要呼叫Alert,就需要一層一層向上呼叫,這個很麻煩的。 所以,在這邊直接使用一個Vue Event Bus的概念來達成這樣的效果,直接將Alert掛載在vue的原型底下,讓vue的原型直接操作這些Alert。 step1. 首先我們現在components資料夾底下創出一個AlertMessage.vue的元件檔。 接著,將課堂上提供的alert模板貼上去。 step2. 到DashBoard.vue元件檔中,將AlertMessage.vue引入進去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <template> <div> <navbar></navbar> <alert/> // 將alert元件放進來 <div class="container-fluid"> <div class="row"> <sidebar></sidebar> <main class="col-md-9 ml-sm-auto col-lg-10 px-md-4"> <router-view></router-view> </main> </div> </div> </div> </template> <script> import sidebar from './sidebar'; import navbar from './navbar'; import alert from './AlertMessage' // 引入AlertMessage元件檔 export default{ components:{ sidebar, navbar, alert }, created(){ const myCookie = document.cookie.replace(/(?:(?:^|.*;\s*)hextoken\s*=\s*([^;]*).*$)|^.*$/, '$1'); this.$http.defaults.headers.common.Authorization = myCookie; } } </script>
step3. 在AlertMessage.vue元件檔案中,會有一個message的陣列,它裡面的元素都是物件。 物件的屬性值,分別是放訊息內容、css樣式和timestamp。
step4. 接著,我們在src的資料夾底下創一個bus.js的檔案,這個檔案跟App.vue檔案是同一層喔。
step5. 我們在bus.js檔案中,引入vue,並將bus掛載在vue的原型底下。
1 2 3 4 ---bus.js檔案--- import Vue from 'vue' Vue.prototype.$bus = new Vue();
此時,各vue元件就都能提取到$bus
了。
step6. 接著,在main.js檔案中引入剛剛的bus.js檔案。
1 2 ---main.js--- import './bus'
step7. 回到AlertMessage.vue的檔案中,註冊有關eventbus的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ---AlertMessage.vue元件檔--- <script> export default { ---以上筆記省略不寫--- created() { const vm = this; // 自定義名稱 'messsage:push' // message: 傳入參數 // status: 樣式,預設值為 warning vm.$bus.$on('messsage:push', (message, status = 'warning') => { vm.updateMessage(message, status); }); }, }; </script>
你可以看到,我們用on
的方式註冊了一個messsage:push
的方法。
step8. 接著,我們在Products.vue檔案中,利用emit
來使用註冊的messsage:push
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ---Products.vue元件檔--- methods:{ uploadFile(){ console.log(this); const uploadFile = this.$refs.files.files[0]; const vm = this; const formData = new FormData(); vm.Status.isUploading = true; formData.append('file-to-upload', uploadFile); const url = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/upload`; console.log('url',url); this.$http.post(url, formData, { headers:{ 'Content-Type': 'multipart/form-data' } }).then((response)=>{ console.log('report',response.data); vm.Status.isUploading = false; if(response.data.success){ vm.$set(vm.tempProduct, 'imageUrl', response.data.imageUrl) console.log(response.data); } else { vm.$bus.$emit('messsage:push', response.data.message, 'danger'); // 加入將錯誤訊息丟到event bus來送出訊息 } }) } }
這個時候,你可以試著將一個svg格式的圖片丟上去,就會傳出相對應的錯誤訊息囉。
產品列表的分頁邏輯 step1. 我們先把後端傳過來的有關分頁的內容存下來。 首先,我們在Products.vue元件檔中,新增pagination的物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ---Products.vue--- export default{ data(){ return { pagination:{}, // 新增pagination物件 products:[], tempProduct:{}, isNew: false, isLoading:false, Status:{ isUploading:false } } }, methods:{ getProducts(){ const vm = this; //const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products/all`; // 原本的商品api註解掉 const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products`; // 修改取得商品的api vm.isLoading = true; this.$http.get(api).then((response) => { console.log(response.data); vm.isLoading = false; vm.products = response.data.products; vm.pagination = response.data.pagination; // 取得伺服器端的pagination }) }, }, created(){ this.getProducts(); } }
你可以看到我們將取得所有商品的api網址改掉了,因為,原本的api網址是不會回傳有關分頁的資訊的。 step2. 將bootstrap的分頁模板,複製下來 然後,貼到Products.vue元件裡面,接著,再用v-for
遍歷總頁碼,製作出所有的分頁出來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li class="page-item" v-for="page in pagination.total_pages" :key="page"><a class="page-link" href="#">{{page}}</a></li> <li class="page-item"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </template>
這樣一來就可以成功做出頁碼囉。
step3. 製作’當前頁面’的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li class="page-item" v-for="page in pagination.total_pages" :key="page" :class="{'active':pagination.current_page===page}"><a class="page-link" href="#">{{page}}</a></li> <li class="page-item"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </template>
利用v-bind
動態綁定class並判斷current_page來達到這種效果。
step4. 製作有無上一頁和下一頁的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item" :class="{'disabled':!pagination.has_pre}"> <a class="page-link" href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li class="page-item" v-for="page in pagination.total_pages" :key="page" :class="{'active':pagination.current_page===page}"><a class="page-link" href="#">{{page}}</a></li> <li class="page-item" :class="{'disabled':!pagination.has_next}"> <a class="page-link" href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </template>
利用以上的方式判斷pre和next是否為false
,若是false
的話,就為它套上disabled
的className和無法被選取的效果。
step5. 製作利用分頁跳轉頁面的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ---Products.vue--- <template> <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item" :class="{'disabled':!pagination.has_pre}"> <a class="page-link" href="#" aria-label="Previous" @click.prevent="getProducts(pagination.current_page-1)"> <span aria-hidden="true">«</span> </a> </li> <li class="page-item" v-for="page in pagination.total_pages" :key="page" :class="{'active':pagination.current_page===page}"> <a class="page-link" href="#" @click.prevent="getProducts(page)">{{page}}</a> </li> <li class="page-item" :class="{'disabled':!pagination.has_next}"> <a class="page-link" href="#" aria-label="Next" @click.prevent="getProducts(pagination.current_page+1)"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </template> <script> methods:{ getProducts(page = 1){ const vm = this; const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products?page=${page}`; vm.isLoading = true; this.$http.get(api).then((response) => { console.log('response',response.data); vm.isLoading = false; vm.products = response.data.products; vm.pagination = response.data.pagination; }) } } </script>
a. 你可以看到我們先把跳轉頁面的api換成六角提供的跳轉到特定頁面的api。 b. 接著,我們在getProducts方法加入參數page並給它預設值為1。 c. 我們在各分頁上都用@click
綁定連動到getProducts方法,藉此來跳轉到特定頁面。 d. 在前一頁和後一頁的按鈕上也綁定連動到getProducts的方法,但是,傳入的參數是利用pagination.current_page加一或減一來達到回上一頁和跳下一頁的功能。