# [Vue cli] 電商網站章節筆記 ###### tags: `Vue.js` ### 新增路由路徑及連結 ![](https://i.imgur.com/Crz7Ngh.png) `<router-link>`組件支持用戶在具有路由功能的應用中(點擊)導航。通過to屬性指定目標地址,默認渲染成帶有正確鏈接的<a>標籤,可以通過配置tag屬性生成別的標籤.。另外,當目標路由成功激活時,鏈接元素自動設置一個表示激活的CSS類名。 `<router-link>`比起寫死的`<a href="...">`會好一些,理由如下: 無論是HTML5 history 模式還是hash 模式,它的表現行為一致,所以,當你要切換路由模式,或者在IE9 降級使用hash 模式,無須作任何變動。 在HTML5 history模式下,router-link會守衛點擊事件,讓瀏覽器不再重新加載頁面。 當你在HTML5 history模式下使用base選項之後,所有的to屬性都不需要寫(基路徑)了 ![](https://i.imgur.com/T0IR1Lf.png) ### router-view應用解析 https://dotblogs.com.tw/wasichris/2017/03/06/235449 ### 製作巢狀路由 https://ithelp.ithome.com.tw/articles/10203823 main.js : 將install的配置檔載入import並開啟 ![](https://i.imgur.com/rFdO5dB.png) App.vue : 顯示頁面的主內容導入 ![](https://i.imgur.com/g9xSOWm.png) index.js : import工具及vue分頁,並export分頁內容 ![](https://i.imgur.com/HJC39Dp.png) ![](https://i.imgur.com/ZBNrAaD.png) ## bootstrap套入方式 https://vue-loader.vuejs.org/zh/guide/pre-processors.html#sass ``` <style lang="scss"> /* 在这里撰写 SCSS */ </style> ``` ## 反引號使用時機 載入API 及 router.push裡的路徑需使用反引號 ``` const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/order`; ``` ``` vm.$router.push(`/customer_checkout/${response.data.orderId}`) ``` ## scoped 在vue文件中的style標籤上,有一個特殊的屬性:scoped。當一個style標籤擁有scoped屬性時,它的CSS樣式就只能作用於當前的組件,也就是說,該樣式只能適用於當前組件元素。通過該屬性,可以使得組件之間的樣式不互相污染。如果一個項目中的所有style標籤全部加上了scoped,相當於實現了樣式的模塊化 ## 使用axios串API ![](https://i.imgur.com/7TOEM7J.png) * axios語法導入API公式, * 前方為伺服器路徑,後方signin為指定功能(後端寫入對應功能取得資料) * this.$http.指定動作post(api取得上方user回傳資料) * 經由判定式導入並push指定路由路徑 ``` this.$http.get(api).then((response) => { console.log(response.data) }) ``` ![](https://i.imgur.com/0z2RHuH.png) ``` [API]: /signin [方法]: post [參數]: { "username": "hexscholl@test.com", "password": "zzxxccvv" } [成功回應]: { "success": true, "message": "登入成功", "uid": "XX4VbV87lRRBXKhZKT7YX6zhsuO2" } [失敗回應]: { "success": false, "message": "登入失敗" } ``` ## 暫存至前端記憶體裡 main.js ![](https://i.imgur.com/OmMI5DU.png) login頁新增admin ![](https://i.imgur.com/yn8KS4Y.png) ## 將各個vue頁面組合方法 * 在script裡import各元件vue,並套用公式輸出 * 在template裡新增<元件名稱></元件名稱> * <Sidebar></Sidebar> 也可以等同 <Sidebar/> ![](https://i.imgur.com/6SdonAX.png) ## 路由巢狀寫法 * 新增children並將vue子路徑放入陣列中 ![](https://i.imgur.com/D2djrJI.png) ## 分號逗號運用時機 逗號運算符是可以在表達式中使用的運算符。它用於分離多個不同的表達式,並具有“評估所有以下表達式,然後產生最終表達式的值”的含義。 分號不是運算符,不能在表達式中使用。它用作JavaScript語法的一部分,以標記被視為語句的表達式的結尾。 ## 儲存API路徑 在API裡需放入路徑以及申請名稱,當需要更改時會難以管理,因此將API所需的資料放入A資料夾以作即時更改 * 在config資料夾裡的dev.env內建立代數名稱 * 如專案正式執行prod.env也要同步建立 ![](https://i.imgur.com/ChRtVnz.png) * API代稱放入即可 ![](https://i.imgur.com/OPmNBQg.png) ## 使用API採post行為之流程 目的:將新增產品的項目匯入到後端上, ![](https://i.imgur.com/ZhC1JP2.png) 依據API後端的格式做調整,括號包住data的產品內容 ![](https://i.imgur.com/B9VYM9q.png) ![](https://i.imgur.com/3uJ5EJN.png) ![](https://i.imgur.com/aZeJ0PW.png) 使用bootstrap公式的hide點擊後關閉 回傳產品現有內容,就會顯示新增的商品 ![](https://i.imgur.com/Ydl8qgO.png) ## KPI匯入方式 * post 新增到資料庫裡 * get 從資料庫取得資料 * put 修改資料庫裡的資料(玄) ## 刪除鍵執行方式 1. 在頁面上新增刪除的按鈕 ![](https://i.imgur.com/BQ55EoX.png) 2. 採用jquery事件觸發開啟,新增openDelModal來使用參數item來讓程式辨別哪一筆資料,並觸發"API觸發事件" ![](https://i.imgur.com/DKnBJGN.png) 3. 串連API刪除事件(API本身已寫好刪除功能,無需再寫一次) ![](https://i.imgur.com/BVULPQb.png) ## 表格匯入API資料 使用 ![](https://i.imgur.com/N9S6gMX.png) * username 是API資料裡提供 * Chris是新增變數在指出圖片放置位置 ![](https://i.imgur.com/d3RUmv1.png) * 在這裡命名url取代api為串接的元素,放入指定process.env.APIPATH路徑 * https://developer.mozilla.org/en-US/docs/Web/API/FormData/append * 採用方法post方法將值帶入,注意這邊要帶入的東西較多 * 最後使用set強制匯入載入資料 ## 取得瀏覽圖示的兩個方法 1. Vue Loading Overlay Component https://www.npmjs.com/package/vue-loading-overlay * 下載npm後在main.js放入套件並啟用在全域,至Vue頁面上放置在templete下方,並新增布林值變數,並在載入API動作前開啟,在完成載入後關閉,如此一來當畫面在瀏覽狀態時就會出現loading的圖示 2. fontawesome icon https://fontawesome.com/icons/spinner?style=solid * 獨特的設定位置:將gdn放入index.html的Title下方即可使用 * data新增變數(代布林值)後,放入指定位置設定開啟與結束時機 補充:如果要點擊到特定產品後產生圖示,便可帶入參數id,使用判定式當產品ID相同時就會顯示 ``` <button type="button" class="btn btn-outline-secondary btn-sm" @click ="getProduct(item.id)"> <i class="fas fa-spinner fa-spin" v-if="status.loadingItem === item.id"></i> 查看更多 </button> ``` * 將變數loadingItem指向產品id,記得結束時要使其變空物件。 ``` getProduct(id){ //click開啟model的動作,並取得既存資料 const vm = this; const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/product/${id}`; vm.status.loadingItem =id; this.$http.get(api).then((response) => { //console.log(response); vm.product = response.data.product; //將取得資料的products項目匯入data的空陣列裡 $('#productModal').modal('show'); vm.status.loadingItem =''; }); }, ``` ## Alert錯誤提示 https://github.com/a-howw/HexSchool_Vue.js/issues/12 ## 多個Vue及js功能載入過程 第一種情況,將Vue分成多元件再組合成一個主頁面上 第二種情況,將js功能載入主頁面上的DOM元素做使用 ### 彙整Vue子元件 1. 新增子Vue元件 2. 在主頁面使用import匯入路徑,並export連結物件 ``` import Pagination from '@/components/Pagination'; export default { components:{ Pagination, }, ``` ## Filter載入js功能 將所需的功能個別分配多個js中,頁面需要用上時便可直接在匯入使用 1. 新增js元件 1. 在main.js檔案裡使用import匯入路徑,並使用對應method開啟 1. 再將宣告子令currency放入主頁面上的DOM元素上 ``` import currencyFilter from './filters/currency'; Vue.filter('currency',currencyFilter); //全域啟用瀏覽中的樣式 ``` 匯入方式如下以|直線加上命名變數currency,數字便會自帶百位小數點 ``` <td class="text-right"> {{item.total| currency}} </td> ``` ## 依照API需求回傳參數 ``` [API]: /api/:api_path/cart [方法]: post [參數]: { "data": { "product_id":"-L9tH8jxVb2Ka_DYPwng","qty":1 } } [成功回傳]: { "success": true, "message": "已加入購物車", "data": { "product_id": "-L9tH8jxVb2Ka_DYPwng", "qty": 1, "coupon_code": "", "id": "-LAl5v_2MhWeh3linQxx", "total": 600, "final_total": 600, "product": { "category": "衣服3", "content": "這是內容", "description": "Sit down please 名設計師設計", "id": "-L9tH8jxVb2Ka_DYPwng", "imageUrl": "test.testtest", "is_enabled": 1, "num": 1, "origin_price": 500, "price": 600, "title": "[賣]動物園造型衣服3", "unit": "個" } } } ``` * 因KPI記載需取得兩種以上的資料,因此使用物件{}的方式載入相關參數 * 再依照指示將資料匯入post(api,{data:cart}) ![](https://i.imgur.com/0LIsQSA.png) ## 提示欄位不完整 vee-validate 參考資料:https://hao1229.github.io/2019/08/09/EcommercePractice8/ #### 將英文提示字幕,轉換為中文語系 1. 安裝 vue-i18n 在 terminal 中輸入 'npm install vue-i18n --save' 2. 在 main.js 中將 vue-i18n import 進來 import VueI18n from 'vue-i18n';Vue.use(VueI18n); 3. 將 VeeValidate.Validator.localize('zh_TW', zhTWValidate) 及 Vue.use(VeeValidate) 刪除,並加入下列程式碼 ``` const i18n = new VueI18n({ locale: 'zhTW' }); Vue.use(VeeValidate, { i18n, dictionary: { zhTW } }); ``` 4. 在 Vue 物件中新增 i18n ``` new Vue({ i18n, el: '#app', components: { App }, template: '<App/>', router, }) ``` #### 執行提示姓名欄位未輸入 * 載入v-validate="'required'" * 顯示地點放入v-if="errors.has('name')"當欄位未填空系統為true ![](https://i.imgur.com/rO5nj6g.png) #### 執行提示信箱欄位未輸入 * 信箱欄位可使用{{ errors.first('email') }}系統協助判定email格式是否正確 ![](https://i.imgur.com/vPmNs5v.png) #### 執行所有欄位未輸入的項目 * 驗證所有欄位都完成才能匯入API公式 * 原先chrome內建自動判定提示(input加上required的前提) * 導入post函式裡的先匯入下列公式,再採用判斷式if=true才發送API ![](https://i.imgur.com/qmIQoYB.png) ``` this.$validator.validate().then((valid) => { if (valid) { this.$http.post(api , {data:order}).then((response) => { console.log("訂單已建立" , response); if (response.data.success) { vm.$router.push(`/customer_checkout/${response.data.orderId}`); //傳至路徑,取得後端資料庫裡的orderId } vm.isLoading = false; }); } else { console.log('欄位不完整'); } }); ``` ## 導航守衛 請參考[官方資料](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB) * 新增判定公式至main.js頁面,並載入API驗證使用者是否已登入過,如果已完成登入,系統便會放行至指定頁面,如果還未便會重新導入登入頁面。 ``` router.beforeEach((to, from, next) => { console.log('to', to, 'from', from, 'next', next); // ... if (to.meta.requiresAuth) { const api = `${process.env.APIPATH}/api/user/check`; axios.post(api).then((response) => { console.log(response.data); if (response.data.success) { next(); } else { next({ path: '/login', }); } }); } else { next(); } }); ``` * 到router頁面新增`meta:{requiresAuth:true}`需驗證的指定vue元件 * 當使用者輸入錯誤網址時,使用redirect語法轉為登入頁面 ``` export default new Router({ routes: [ { path: '*', redirect: 'login', }, { path: '/', name: 'HelloWorld', component: HelloWorld, meta: { requiresAuth: true }, }, ``` ## 套用bootstrap版型(載入模板css / html) 在Boostrap範例所取得的css以及html的原始碼套入vue cli裡做使用 CSS 第一步:建立一個dashboard.scss將css放入 第二步:將上方scss載入all.scss作統一管理import 第三步:all.scss會串連至預設的app.vue裡啟動 補充:如vue元件不想被全域scss所應用,需在style標籤增加scoped,便可獨立應用 template 第一步:新增dashboard主頁面,再分離sidebar以及navbar兩個元件 第二步:兩元件載入主頁面,需要做1. import呼叫,2.啟用,3.掛在template ## 使用openModel開啟彈跳視窗 在Methods內寫入函式 > 需將import $ from 'jquery';匯入scrpit裡來啟動jquery變數元件$ ``` openModal(){ $('#productModal').modal('show'); } ``` 亦可帶入參數或加入判斷式等條件來開啟指定Model ``` openModal(isNew , item){ if(isNew){ this.tempProduct= {}; this.isNew=true; }else{ this.tempProduct= Object.assign({},item); //es6語法,為了避免item與左方物件相同 this.isNew=false; } $('#productModal').modal('show'); }, ``` ## 製作分頁鍵 後端將每項產品都標記好頁數、目前頁數等等資訊,我們只要透過getProduct取得API資訊後,代入頁數page將分頁的資訊回傳到變數中,就可以操控並設計分頁元件 * 回傳給變數pagination ``` getProducts(page = 1){ //ES6預設值,如無代數值便會使用原先1,有參數則使用參數數值 const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products?page=${page}`; const vm = this; vm.isLoading = true; this.$http.get(api).then((response) => { console.log(response.data); vm.isLoading= false; vm.products = response.data.products //將取得資料的products項目匯入data的空陣列裡 vm.pagination = response.data.pagination; }); }, ``` 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">&laquo;</span> <span class="sr-only">Previous</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">&raquo;</span> <span class="sr-only">Next</span> </a> </li> </ul> </nav> ``` > 為了增加使用的便利性,建議將頁籤寫成元件,在合適的頁面上直接套用即可 ## 問號區 當產品圖片要借用for迴圈產生的item.url時,就可以使用動態:style並匯入下方公式 ``` <div style="height: 400px; background-size: cover; background-position: center" :style="{backgroundImage:`url(${item.imageUrl})`}" > <!-- 插入連結的方式,上方為ES6反引號插入大括號的用法 --> ``` ## 網址帶入產品ID 在router裡新增路徑,注意要將後方代的產品編號對應後端的id項目 ``` { path:'customer_checkout/:orderId', name:'CustomerCheckout', component: CustomerCheckout, }, ``` 在vue購物元件裡執行post的購物頁面,當傳送成功後便將ID傳入上方路徑中 ``` if (response.data.success) { vm.$router.push(`/customer_checkout/${response.data.orderId}`); //傳至路徑,取得後端資料庫裡的orderId } ``` 開啟新增的Vue頁面,在底部新增params路由公式,將WWW上的ID匯入空陣列上使用,經上面步驟後,便可以使用get取得這筆ID的訂單明細,變相告知後端資料庫要取得哪一筆資料,相似於代參數。 ``` created(){ this.orderId = this.$route.params.orderId; //匯入參數公式之一, oderId必須要對應router裡的path路徑名稱 console.log(this.orderId); this.getOrder(); } ``` 在取得資料時將ID傳入,以便資料庫知道調出特定資料至頁面上 ``` payOrder(){ const vm = this; const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/pay/${vm.orderId}`; vm.isLoading = true; this.$http.post(api).then((response) => { console.log(response); if(response.data.success){ vm.getOrder() //一旦點擊,後端系統便會將is_paid轉為true } vm.isLoading = false; }); }, ``` 總結就是利用WWW網址上的數值,將資料傳送到另一個vue元件上使用。 ## VeeValidate 表單欄位的守護者 功能如下 * 表單輸入空白會顯示自定義的提醒說明 * email有專屬的程式碼,會針對輸入細節提醒 * email的字定義的提醒文字,需載入中文版 * 送出表單如有required程式碼,chrome瀏覽器會偵測欄位錯誤 * 如要使用內建的submit驗證,官網裡有相關的程式碼可以載入,並將其放入api傳送的函式裡做判定 官方資料:http://vee-validate.logaretm.com/v2/guide/events.html#changing-default-events 影片解說:https://www.udemy.com/course/vue-hexschool/learn/lecture/10896798#notes