0%

Vue-vue出一個電商網站-撰寫DashBoard

套用 Bootstrap Dashboard 版型

step1.
首先,你可以先去Bootstrap的公版的網站 中,選取Dashboard的主題,來當作我們的Dashboard的版型。

step2.
接著,你點開Dashboard的主題,然後對這個網頁點擊右鍵查看原始碼,為的是要將這個Dashboard的html程式碼抓下來。

step3.
去你的專案檔中的assets資料夾目錄底下,新增一個_Dashboard.scss檔案,
接著,去你剛剛在察看Dashboard原始碼的頁面中,找尋dashboard.css這個關鍵字,
並把這個超連結打開,裡面的內容就是這個Dashboard所用到的css樣式。

step4.
將你剛剛開的dashboard.css裡面的內容,複製起來貼到你的_Dashboard.scss檔案中。
接著,在all.scss檔案中將這個_Dashboard.scss檔案給import進來。

step5.
去components資料夾底下,新增一個DashBoard.vue的元件檔
並將dashboard公版內的html內容加入到你的DashBoard.vue的元件檔中。
記得,在公版的script部分的內容是不需要複製的喔,因為,我們只有要用html的內容部份。

step6.
在index.js檔案中匯入剛剛新增的DashBoard.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
34
35
---index.js檔案---
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import DashBoard from '@/components/DashBoard' // 引入DashBoard元件
import Login from '@/components/pages/login'


Vue.use(Router)

export default new Router({
routes: [
{
path:'*',
redirect:'login'
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld,
meta: { requiresAuth: true }
},
{
path:'/login',
name: 'Login',
component: Login
},
{
path: '/admin', // DashBoard的路徑這些屬性
name: 'HelloWorld',
component: DashBoard,
meta: { requiresAuth: true } // 它是需要被驗證的
}
]
});

step7.
這個時候,我們會將DashBoard元件檔中,原本含有的sidebar和navbar的元件內容,先拆解成兩個元件檔。
所以,你要在components資料夾中新增兩個元件檔分別為sidebar.vue 和 navbar.vue
然後,將原本的內容分別拆解到這兩個元件檔中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---navbar.vue元件檔---
<template>
<div>

<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">Company name</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="#">Sign out</a>
</li>
</ul>
</header>
</div>

</template>

step8.
將原本在dashboard.vue中的main標籤裡面的內容都刪掉,因為,我們要加入自己的內容。
接著在,DashBoard.vue中引入,navbar和sidebar元件。
並將這兩個元件塞回原本在DashBoard的html的位置。

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
---DashBoard.vue元件檔---
<template>
<div>
<navbar/> // 塞入navbar元件
<div class="container-fluid">
<div class="row">
<sidebar></sidebar> // 塞入sidebar元件

<main class="col-md-9 ml-sm-auto col-lg-10 px-md-4">

</main>
</div>
</div>
</div>
</template>

<script>
import sidebar from './sidebar';
import navbar from './navbar';
export default{
components:{
sidebar,
navbar
}
}

</script>

你可以看到在template部分,我們加入sidebar和navbar的標籤的方式不同,其實,它們的效果是一樣的,
只是寫法不一樣而已~~

step9.
現在要加入塞在DashBoard.vue中的內容。
我們先在pages資料夾中,新增一個Products.vue的元件檔。
然後裡面的內容先隨便亂塞,只讓它先可以呈現在畫面中即可。

1
2
3
4
5
6
---Products.vue元件檔---
<template>
<div>
123
</div>
</template>

step10.
我們到DashBoard.vue元件檔的main標籤裡面新增一個router-view

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
---DashBoard.vue元件檔---
<template>
<div>
<navbar/>
<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';
export default{
components:{
sidebar,
navbar
}
}

</script>

注意~~~~~~~~~~~~
目前,這個時候router-view是巢狀的,
一個是在App.vue裡面,那另一個就是在DashBoard.vue裡面。

所以,我們現在要製作的巢狀的router
所以,我們先在到router資料夾中index.js中,來為DashBoard元件製作巢狀的router

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
38
39
40
41
42
43
44
---DashBoard.vue元件檔---
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import DashBoard from '@/components/DashBoard'
import Login from '@/components/pages/login'
import Products from '@/components/pages/Products'

Vue.use(Router)

export default new Router({
routes: [
{
path:'*',
redirect:'login'
},
{
path: '/',
name: 'HelloWorld',
component: HelloWorld,
meta: { requiresAuth: true }
},
{
path:'/login',
name: 'Login',
component: Login
},
{
path: '/admin',
name: 'HelloWorld',
component: DashBoard,
meta: { requiresAuth: true },
children:[
{
path:'products',
name: 'Products',
component: Products,
meta: { requiresAuth: true }, // Products頁面也需要驗證才能進來
}
]
}
]
});

你可以看到我們為DashBoard加入了巢狀的router囉,並且要在登入的狀態才能進入
這個products的頁面喔。

製作產品列表

step1.
首先,我們先修改原本login的登入頁面,原本login成功後,會直接進到首頁,但是這不是我們想要的,
我們是希望它能跳轉到products的頁面。
所以,我們要修改一下,router資料夾中index.js的檔案

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
---index.js---
import Vue from 'vue'
import Router from 'vue-router'
//import HelloWorld from '@/components/HelloWorld'
import DashBoard from '@/components/DashBoard'
import Login from '@/components/pages/login'
import Products from '@/components/pages/Products'

Vue.use(Router)

export default new Router({
routes: [
{
path:'*',
redirect:'login'
},
{
path:'/login',
name: 'Login',
component: Login
},
{
path: '/admin',
name: 'HelloWorld',
component: DashBoard,
meta: { requiresAuth: true },
children:[
{
path:'products',
name: 'Products',
component: Products,
meta: { requiresAuth: true },
}
]
}
]
});

先把原本的Helloworld的元件拿掉。
接著,到login.vue元件檔中,修改login成功後,會跳轉的頁面路徑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---login.vue元件檔
methods:{
signin:function(){
const api = `${process.env.APIPATH}/admin/signin`;
const vm = this;
this.$http.post(api, vm.user).then((response) => {
console.log(response.data);
if(response.data.success){

const token = response.data.token;
const expired = response.data.expired;
console.log(token, expired);
document.cookie = `hextoken=${token}; expires=${new Date(expired)}`;
vm.$router.push('/admin/products'); // 修改成直接跳轉到products的頁面
}
})
}
}

step2.
接下來要取得遠端資料。
你就先在Products.vue元件檔中,新增取得資料的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---Porduct.vue元件檔---
<script>
export default{
data(){
return {
products:[],
}
},
methods:{
getProducts(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products/all`;
this.$http.get(api).then((response) => {
console.log(response.data);
})
}
},
created(){
this.getProducts()
}
}

</script>

你可以看到,我們還有再為Products元件新增created的hook,在該此元件被建立的時候,來取得資訊。

step3.
接著你打開log應該就可以看到,有取回資料了,但是,現在是取到兩份相同的資料,
原因是再App.vue中,也有取得後端資料的程式碼,那部分就可以刪掉了,只要留
Products.vue元件檔這部分就可以了。

step4.
接下來,要將取出來的token往後端發送。
所以,我們要將這段程式碼加入到DashBoard.vue元件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---DashBoard.vue元件檔---
<script>
import sidebar from './sidebar';
import navbar from './navbar';
export default{
components:{
sidebar,
navbar
},
created(){
const myCookie = document.cookie.replace(/(?:(?:^|.*;\s*)hextoken\s*=\s*([^;]*).*$)|^.*$/, '$1');
console.log(myCookie,'myCookie');
}
}

</script>

你可以看到我們為DahBoard的元件中加入了mycookie把cookie的值塞進去,那中間有一個hextoken,那個就是你在login
自訂義的cookie名稱。
接下來,重新刷新頁面,應該就可以在console.log看到你的mycookie的值囉。

step5.
接著,在vue axios的github中,
有提供instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
這段程式碼。
它是代表,我之後加入的請求,都會預設加入這個欄位。
把mycookie往後端送。
接下來我們將defaults.headers.common['Authorization'] = AUTH_TOKEN;的內容加到DashBoard中。
記得,不用把上面哪程式碼中的instance的部分也貼進來。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---DashBoard.vue元件檔---
<script>
import sidebar from './sidebar';
import navbar from './navbar';
export default{
components:{
sidebar,
navbar
},
created(){
const myCookie = document.cookie.replace(/(?:(?:^|.*;\s*)hextoken\s*=\s*([^;]*).*$)|^.*$/, '$1');
this.$http.defaults.headers.common.Authorization = myCookie;
}
}

</script>

加入以上的內容之後,應該就可以看到在後台頁面成功的回傳從伺服器端回傳的內容囉。
你可以打開開發者介面,然後,在NetWork的頁面中的All選項,裡面有一個websocket。
你可以看到一個Request Header的部分,裡面的Cookie的值,就是你從前端傳到後端的內容。
request header

step6.
首先,我們要先去六角提供的後台中,取得商品列表部分
看我們可以取得什麼回傳的資訊,
然後,在因應這些資訊去你的products的table欄位裡面,去新增相對應的tr

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
---producs.vue元件檔---
<template>
<div>
<div class="text-right mt-4">
<button class="btn btn-primary">建立新的產品</button>
</div>
<table class="table mt-4">
<thead>
<th width="120">分類</th>
<th>產品名稱</th>
<th width="120">原價</th>
<th width="120">售價</th>
<th width="100">是否啟用</th>
<th width="80">編輯</th>
</thead>
<tbody>
<tr v-for="(item, key) in products" :key="key">
<td>{{item.category}}</td>
<td>{{item.title}}</td>
<td class="text-right">
{{item.origin_price}}
</td>
<td class="text-right">
{{item.price}}
</td>
<td>
<span v-if="item.is_enabled" class="text-success">啟用</span>
<span>未啟用</span>
</td>
<td>
<button class="btn btn-outline-primary btn-sm">編輯</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>

以上就是,我們從後端部分取得資料後,將裡面的資料貼到前台的畫面中。
另外,你可以注意到,在thead中,我們有對某些th直接設定width的數值,那剩下的沒有被設定的部分,
瀏覽器會直接自動分配給它。
注意~~~ 價錢都要靠右喔。

Vue 中運用 Bootstrap 及 jQuery

step1.
這邊我們要製作的內容,是bootstrap中提供的Modal的互動效果,所以,我們需要安裝bootstrap的js的部分。
首先,先去main.js檔案中引入bootstrap

1
2
3
4
5
6
7
---main.js檔案---
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import 'bootstrap' // 引入bootstrap的檔案
import App from './App'
import router from './router'

但是,這個時候,你編譯完成後編譯器應該會跳錯,要求你要安裝jQ和popper,
因為,bootstrap.js是有用到這兩個套件的。
你就輸入提示指令npm install --save jquery popper.js
安裝完之後,再重新運行vue環境,此時,應該就不會再跳錯囉。

step2.
接下來將bootstrap的modal的元件加到products.vue中囉。
那因為,我們希望在完整的接收完後端的資料之後,才將Modal完整的秀出來。
所以,這邊我們會搭配vue的方式,來開啟modal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---products.vue元件檔---
<template>
<button type="button" class="btn btn-primary" @click="openModal">
建立新產品
</button>
</template>

<script>
export default{
methods:{
---筆記省略以上的內容---
openModal(){
$('#productModal').modal('show');
}
},
created(){
this.getProducts()
}
}

</script>

你可以看到,我們在products.vue中新增了openModal方法,藉此透過按下建立新產品按鈕來跳出Modal。

step3.
但是,這個時候你按下建立新產品的按鈕時,Modal不會跳出來,你打開開發者介面,
會發現$is not defined的錯誤。
雖然,我們在main.js引入bootstrap時,有安裝了jQ了,但是,這個jQ只是這個bootstrap的相依套件,
跟products.vue元件一點干係都沒有,所以,我們需要在products.vue檔案中,
再引入一次jQ的檔案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---products.vue元件檔---
<template>
<button type="button" class="btn btn-primary" @click="openModal">
建立新產品
</button>
</template>

<script>
import $ from 'jquery' // 把$從jquery引入進來
export default{
methods:{
---省略以上的內容---
openModal(){
$('#productModal').modal('show');
}
},
created(){
this.getProducts()
}
}

</script>

你可以看到我們從jquery檔案中引入了$,此時,你再按下建立新產品的按鈕,就可以成功執行Modal囉~~
那我們透過這種方式觸發Modal的話,就可以自己決定要在Ajax完成讀取後,才觸發這個Modal囉~~

產品的新增修改

step1.
這邊老師提供了Modal內的模板 ,你只要直接把它套入建立新產品按鈕觸發的Modal的內容,就ok囉,不用自己再打一遍。

step2.
我們在product.vue元件檔中,新增一個tempProduct的物件,並將這個物件的屬性綁定到我們剛剛在Modal
裡面新增的form內的各欄位中。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
---product.vue 元件檔---
<div class="modal fade" id="productModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content border-0">
<div class="modal-header bg-dark text-white">
----筆記這邊省略不寫---
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-4">
----筆記這邊省略不寫---
</div>
<div class="col-sm-8">
----筆記這邊省略不寫---

<div class="form-group">
<label for="content">說明內容</label>
<textarea type="text" class="form-control" id="content"
placeholder="請輸入產品說明內容" v-model="tempProduct.content"></textarea>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="is_enabled" v-model="tempProduct.is_enabled" :true-value="1" :false-value="0">
<label class="form-check-label" for="is_enabled">
是否啟用
</label>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" @click="updateproduct">確認</button>
</div>
</div>
</div>
</div>

<script>
import $ from 'jquery';
export default{
data(){
return {
products:[],
tempProduct:{} // 新增tempProduct物件
}
},
methods:{
getProducts(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products/all`;
this.$http.get(api).then((response) => {
console.log(response.data);
vm.products = response.data.products;
})
},
openModal(){
$('#productModal').modal('show');
},
updateproduct(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/product`;
this.$http.post(api, {data: vm.tempProduct}).then((response) => {
console.log(response.data);
})
}
},
created(){
this.getProducts()
}
}

</script>

a. 以上你可以看到我們在各個input欄位上面綁定了tempProduct的各屬性。
而要特別注意的是是否啟用的欄位,除了用v-model去綁定tempProduct的屬性之外,我們還特別用
v-bind去綁定true-valuefalse-value各代表什麼值喔。
b. 你可以看到確認按鈕會綁定一個方法叫做updateproduct

step3.
上面有提到確認按鈕有綁定updateproduct的方法,那在課堂上有提供商品建立的api
,那這邊要特別注意的是api的路徑要改一下,而且它的vue.axios的方法是用post喔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---product.vue元件檔---
<script>
methods:{
getProducts(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/products/all`;
this.$http.get(api).then((response) => {
console.log(response.data);
vm.products = response.data.products;
})
},
openModal(){
$('#productModal').modal('show');
},
updateproduct(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/product`;
this.$http.post(api, {data: vm.tempProduct}).then((response) => {
console.log(response.data);
})
}
},
</script>

傳給後端的資料格式

以上的範例中你要注意一下,在vue.axios中的post,我們不只有傳送api,我們還傳送了tempProduct物件過去,
那為什麼在tempProduct前面還要再加入data屬性呢,那是因為api取得資料的格式的原因。
在商品建立的api的參數欄位中,它的內容是這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[參數]:
{
"data": {
"title": "[賣]動物園造型衣服3",
"category": "衣服2",
"origin_price": 100,
"price": 300,
"unit": "個",
"image": "test.testtest",
"description": "Sit down please 名設計師設計",
"content": "這是內容",
"is_enabled": 1,
"imageUrl": <<firebase storage url>>
}
}

你可以看到,是最外層有一個物件,然後,它有一個data的屬性,然後,在data裡面才是各個資料的值。
這就是為什麼我們要用這種方式傳遞的原因喔。

step4.
此時,當你按下建立新產品的按鈕時,你在跳出來的Modal中的標題中,先輸入”新增產品”,接著,按下確認。
此時,你看開發者頁面中的console中,應該會印出已建立產品的內容。
接著,重新刷新頁面,你應該就可以看到畫面上有新增你剛剛新增的產品項目囉。

step5.
然後,我們在updateproduct這個方法中,再新增一些處理的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---product.vue元件檔---
methods:{
updateproduct(){
const vm = this;
const api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/product`;
this.$http.post(api, {data: vm.tempProduct}).then((response) => {
console.log(response.data);
if(response.data.success){
$('#productModal').modal('hide');
vm.getProducts();

} else {
$('#productModal').modal('hide');
vm.getProducts();
console.log('新增產品失敗');
}
})
}
}

以上加入的內容,就是若建立成功的話就將該modal關掉。
若建立失敗,就將該modal關掉,並印出新增產品失敗。

step6.
現在,要加入編輯資料的功能。
所以,我們先用一個isNew的屬性來判斷該資料是否是新的。

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
---product.vue元件檔---
<template>
<div class="text-right mt-4">
<button type="button" class="btn btn-primary" @click="openModal(true)">
建立新產品
</button>

</div>

<td>
<button class="btn btn-outline-primary btn-sm" @click="openModal(false, item)">編輯</button>
<button class="btn btn-outline-danger btn-sm" @click="openDelModal(item)">刪除</button>
</td>
</template>

<script>
data(){
return {
products:[],
tempProduct:{},
isNew: false
}
},
methods:{
openModal(isNew, item){
if(isNew){
this.tempProduct = {};
this.isNew = true;
} else{
this.tempProduct = Object.assign({}, item);
this.isNew = false;
}
}
}
</script>

a.你可以看到,我們修改了openModal的方法內容,並新增了一個isNew的屬性。
那這邊要特別注意的是,因為,js傳遞物件是傳參考的特性,所以,
這邊再做將tempProcut的物件與item參數的內容作傳遞的時候,我們用了Object.assign的方式,
讓只有將item的內容存到tempProduct中,沒有連item的參考都傳過去。
b. 另外,你就可以看到建立新產品 和 編輯這兩個按鈕,在他們綁定的openModal的方法,並丟引入truefalse的參數,
藉此來判斷該品項是舊的資料 或是 新的資料。

那因為,我們這邊做的是修改資料,所以,我們也需要稍微修改一下updateproduct方法內的內容,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---product.vue元件檔---
methods:{
updateproduct(){
const vm = this;
let api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/product`;
let httpMethod = 'post';
if(!vm.isNew){
api = `${process.env.APIPATH}/api/${process.env.CUSTOMPATH}/admin/product/${vm.tempProduct.id}`;
httpMethod = 'put';
}
this.$http[httpMethod](api, {data: vm.tempProduct}).then((response) => {
console.log(response.data);
if(response.data.success){
$('#productModal').modal('hide');
vm.getProducts();

} else {
$('#productModal').modal('hide');
vm.getProducts();
console.log('新增產品失敗');
}
})
}
}

a.
首先,我們將宣告api的方式從原本的const改為let,因為它會被修改到。
b.
接著,當該物件的isNew旗標是false的時候,代表該產品不是新的,就代表我們要編輯它,那編輯它的api是不一樣的,最後結尾要加上該產品的id。
c.
我們先宣告了一個httpMethod的變數,因為,有putpost兩種方法,如果,isNew = false,則httpMethod = ‘put’,
並將最後this.$http[httpMethod] 改成這種形式,來吃到putpost的方法,來決定跟後端的api互動的方式。

串接上傳檔案 API

特別注意,上傳這個行為跟之前的行為不太一樣,
因為,上傳的時候,是需要使用formData來上傳的。
以下這個連結 就有示範。

step1.
首先,你現在之前加的productModal的Modal中的html中的上傳圖片的input欄位裡面新增綁定事件。

1
2
3
4
5
6
7
8
9
10
11
12
<label for="customFile">或 上傳圖片
<i class="fas fa-spinner fa-spin"></i>
</label>
<input type="file" id="customFile" class="form-control" ref="files" @change="uploadFile">

<script>
methods:{
uploadFile(){
console.log(this);
}
}
</script>

先綁定當該欄位有變動的時候,就會觸發uploadFile方法。

你可以看到我們故意先將uploadFile裡面先放入console.log看看當你將圖片拉進該欄位的時候,會出現什麼內容。
當你將圖片拉進去之後,你要去找該物件的$refs->files->files陣列中的第一個元素。
上傳圖片的屬性

step2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
methods:{
uploadFile(){
console.log(this);
const uploadFile = this.$refs.files.files[0];
const vm = this;
const formData = new FormData();
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);
})
}
}

a.
首先,因為上傳需要用FormData的方式上傳,所以,這邊先new了一個FormData的物件。
b.
接著,我們用append方法來為key'file-to-upload'塞入uploadFile的值,也就是我們傳上去的圖片檔案。
c.
我們將六角提供的上傳檔案的api存到url變數當中。
d.
接著,用vue.axiospost方式將圖片上傳上去。
其中,它的第三個引數,有特別新增一個headers屬性,
那這個屬性是Content-type 表頭資訊 (header) 用於定義 資料格式 (data format),
用來告知後端傳過來是甚麼資料格式。
那有關Content-type要怎麼設定的詳細內容,你可以看課程”串接上傳檔案 API”參考這位同學中的發問
有關header的發問
e.
那你可以看到這邊console.log回來的有一個這張圖片的路徑,那接下來我們就要把這個取得的路徑,
塞到tempProduct的圖片中囉。

step3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
methods:{
uploadFile(){
console.log(this);
const uploadFile = this.$refs.files.files[0];
const vm = this;
const formData = new FormData();
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);
if(response.data.success){
vm.tempProduct.imageUrl = response.data.imageUrl;
console.log(vm.tempProduct);
}
})
}
}

你可以看到我們將response.data的imageUrl塞給tempProduct的imageUrl,
這個時候,你去查看vm.tempProduct會發現它的title和id屬性,都有gettersetter的屬性,
唯獨只有imgUrl沒有,導致imageUrl沒有呈現在畫面上,無法做雙向綁定。
imgUrl沒有setter

為imageUrl加上setter

所以,我們要直接為imageUrl增加一個setter方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
methods:{
uploadFile(){
console.log(this);
const uploadFile = this.$refs.files.files[0];
const vm = this;
const formData = new FormData();
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);
if(response.data.success){
// vm.tempProduct.imageUrl = response.data.imageUrl;
// console.log(vm.tempProduct);
vm.$set(vm.tempProduct, 'imageUrl', response.data.imageUrl)
}
})
}
}

你看到我們新增了一個setter的方法給imageUrl屬性值。
並把上面那兩行給槓掉,如此一來當你為某個商品編輯的時候,上傳圖片,就可以順利的上傳圖片囉。