1.使用vue-cli創建前端項目
運用vue-cli工具可以很輕松地構建前端項目,當然,使用webstorm來構建會更加簡潔(如圖)。本文推薦使用webstorm,因為在后續開發中,ide會使我們的開發更加簡潔。部分配置如圖:


2.navbar編寫
作為一個webapp,navbar作為應用的導航欄是必不可少的。在本項目中,筆者引入了bootstrap對navbar進行了輕松地構建。在vue中我們需要在components文件夾中將我們的組件加進去,對于本工程來說,navbar是我們要加入的第一個組件,他獨立于router之外,一直固定在網頁上方。
2.1 首先,我們使用npm來安裝vue,vue-cli,bootstrap
1
2
3
|
npm install vue npm install -g vue-cli npm install --save bootstrap jquery popper.js |
2.2 接下來我們在components目錄下new一個vue組件,并且在main.js中引入bootstrap依賴:
1
2
|
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/js/bootstrap.min' |
2.3 下面就可以開始寫代碼了,由于本文只關注table相關的功能,所以導航欄中除了script意外的元素都已經disable,代碼如下:
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
|
<template> <nav class = "navbar navbar-expand-lg navbar-dark bg-dark" > <span class = "navbar-brand mb-0 h1" >vue-springboot</span> <button class = "navbar-toggler" type= "button" data-toggle= "collapse" data-target= "#navbarnav" aria-controls= "navbarnav" aria-expanded= "false" aria-label= "toggle navigation" > <span class = "navbar-toggler-icon" ></span> </button> <div class = "collapse navbar-collapse" id= "navbarnav" > <ul class = "navbar-nav" > <li class = "nav-item" > <router-link class = "nav-link" to= "/home" >home</router-link> </li> <li class = "nav-item active" > <router-link to= "/" class = "nav-link" >script</router-link> </li> <li class = "nav-item" > <router-link to= "/history" class = "nav-link" >history</router-link> </li> </ul> </div> </nav> </template> <script> export default { name: "mynavbar" } </script> <style scoped> </style> |
2.3 在app.vue中引入mynavbar
3.script table編寫
作為自動化工具,必不可少的一部分就是引入script,我們希望用戶能夠自由地使用h5界面進行script的編寫,因此在這里使用了vue的數據雙向綁定進行table crud。
3.1 新建一個vue組件scripttable,代碼如下:
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
<template> <div class = "container-fluid" id= "scripttable" > <h3>my script</h3> <form style= "margin-top: 1rem" > <input type= "file" @change = "getfile($event)" class = "" multiple/> <input type= "button" value= "upload" @click = "submit($event)" class = "btn btn-dark" > </form> <table class = "table table-hover text-center table-bordered" style= "word-break: break-all; word-wrap: break-word;margin-top: 1rem;" > <thead> <th>#</th> <th>platform</th> <th>action</th> <th>path</th> <th>value</th> <th>wait</th> <th>screenshot</th> <th>change</th> </thead> <tbody> <tr v-cloak v- for = "(item, index) in steps" > <th>{{index+ 1 }}</th> <td>{{item.platform}}</td> <td>{{item.action}}</td> <td>{{item.path}}</td> <td>{{item.value}}</td> <td>{{item.wait}}</td> <td>{{item.screenshot}}</td> <td><a href= "#" v-on:click= "edit(item)" >edit</a> | <a href= "#" v-on:click= 'aaa(index)' >delete</a> </td> </tr> <tr> <th></th> <td><select class = "form-control" v-model= "stepstemp.platform" > <option>web</option> <option>android</option> </select></td> <td><select class = "form-control" v-model= "stepstemp.action" > <option>click</option> <option>get</option> <option>input</option> <option>swipe</option> </select></td> <td><input class = "form-control" v-model= "stepstemp.path" placeholder= "enter the xpath" ></td> <td><input class = "form-control" v-model= "stepstemp.value" placeholder= "enter the input value" ></td> <td><input class = "form-control" v-model= "stepstemp.wait" placeholder= "waiting seconds" ></td> <td><select class = "form-control" v-model= "stepstemp.screenshot" > <option>yes</option> <option>no</option> </select></td> <td> <button class = "btn btn-sm btn-dark" v-on:click= 'save' v- if = "isnotedit" >save</button> <button class = "btn btn-sm btn-primary" v-on:click= 'saveedit' v- else >saveedit</button> </td> </tr> </tbody> </table> <hr/> </div> </template> <script> import vue from 'vue' import axios from 'axios' export default { name: "scripttable" , data() { return ({ steps: [], stepstemp: { platform: '' , action: '' , path: '' , value: '' , wait: '' , screenshot: '' }, isnotedit: true }); }, methods: { save: function () { this .steps.push( this .stepstemp); this .stepstemp = { platform: '' , action: '' , path: '' , value: '' , wait: '' , screenshot: '' }; }, aaa: function (index) { this .steps.splice(index, 1 ) }, edit: function (item) { this .isnotedit = false ; this .stepstemp = item; }, saveedit: function () { this .isnotedit = true ; this .stepstemp = { platform: '' , action: '' , path: '' , value: '' , wait: '' , screenshot: '' }; } } } </script> <style scoped> </style> |
3.3 運行dev,打開localhost:8080
npm run dev
前端頁面效果如下:
至此,本文相關的純前端部分完成地差不多了,加上mock的數據后,我們可以開始進行后端的開發了。
4.使用spring initializr創建后端項目
為了更輕松地構建工程,構建restful api以及更輕松地配置請求處理,筆者選擇了spring boot作為后端框架。
4.1 首先我們使用idea集成的spring initializr來構建項目,部分配置如圖:

4.2 接下來在pom.xml中引入poi依賴,點擊import change。如下所示:
1
2
3
4
5
|
<dependency> <groupid>org.apache.poi</groupid> <artifactid>poi-ooxml</artifactid> <version> 4.0 . 0 </version> </dependency> |
4.3 接下來我們在application.properties中配置server.port=8088,與前端項目分開
5.pojo類step的編寫
下面是對pojo類的編寫,本文所需的pojo只有step一種,與前端的table相對應,代碼如下:
1
2
3
4
5
6
7
8
9
10
|
import lombok.data; @data public class step { private string platform; private string action; private string path; private string value; private int wait; private string screenshot; } |
6.uploadcontroller的編寫
接下來是對前端post請求的handler(controller)進行編寫,我們將上傳這個post請求與"/uploadfile"相對應,注意加入@crossorigin注解實現跨域,代碼如下:
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
|
package com.daniel.vuespringbootuploadbe; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.crossorigin; import org.springframework.web.bind.annotation.postmapping; import org.springframework.web.bind.annotation.responsebody; import org.springframework.web.multipart.multipartfile; import java.io.file; import java.io.ioexception; import java.nio.file.files; import java.nio.file.path; import java.nio.file.paths; import java.util.list; @controller @crossorigin @responsebody public class uploadcontroller { private static string uploaded_folder = "src/main/resources/static/temp/" ; @autowired private loadservice loadservice; @postmapping ( "/upload" ) public list<step> singlefileupload(multipartfile file) { try { // get the file and save it somewhere byte [] bytes = file.getbytes(); path path = paths.get(uploaded_folder + file.getoriginalfilename()); files.write(path, bytes); } catch (ioexception e) { e.printstacktrace(); } // print file data to html list<step> result = loadservice.casttostep( new file(uploaded_folder + file.getoriginalfilename())); return result; } } |
7.loadservice的編寫
下面該編寫service來讀取請求中傳送的文件了,簡單地來說只有一個步驟,將excel中的script轉換為pojo的鏈表并在controller中作為responsebody返回.
7.1 首先創建service接口,代碼如下:
1
2
3
4
5
6
7
8
|
package com.daniel.vuespringbootuploadbe; import org.springframework.stereotype.service; import java.io.file; import java.util.list; @service public interface loadservice { list<step> casttostep(file file); } |
7.2 接下來創建service實現類,代碼如下:
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
|
package com.daniel.vuespringbootuploadbe; import org.apache.poi.openxml4j.exceptions.invalidformatexception; import org.apache.poi.ss.usermodel.row; import org.apache.poi.ss.usermodel.sheet; import org.apache.poi.ss.usermodel.workbook; import org.apache.poi.xssf.usermodel.xssfworkbook; import org.springframework.stereotype.service; import java.io.file; import java.io.ioexception; import java.util.arraylist; import java.util.list; @service public class loadserviceimpl implements loadservice { @override public list<step> casttostep(file file) { list<step> steps = new arraylist<>(); workbook workbook = null ; try { workbook = new xssfworkbook(file); } catch (ioexception e) { e.printstacktrace(); } catch (invalidformatexception e) { e.printstacktrace(); } sheet sheet = workbook.getsheetat( 0 ); int num = sheet.getlastrownum() - sheet.getfirstrownum(); //read steps for ( int i = 0 ; i < num; i++) { row row = sheet.getrow(i+ 1 ); step step = new step(); step.setplatform(row.getcell( 0 ).getstringcellvalue()); step.setaction(row.getcell( 1 ).getstringcellvalue()); step.setpath(row.getcell( 2 ).getstringcellvalue()); step.setvalue(row.getcell( 3 ).getstringcellvalue()); step.setwait(( int ) row.getcell( 4 ).getnumericcellvalue()); step.setscreenshot(row.getcell( 5 ).getstringcellvalue()); steps.add(step); } try { workbook.close(); } catch (ioexception e) { e.printstacktrace(); } return steps; } } |
8.搭建簡單的restful api
文章臨近尾聲,現在前后端的獨立代碼基本開發完畢,是時候搭建restful了,本文中的api非常簡單,就是對上傳做出響應,并將返回的json寫入界面上的table中,完成script導入,npm安裝axios后,在scripttable組件中加入如下代碼:
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
|
getfile: function (event) { this .file = event.target.files[ 0 ]; console.log( this .file); }, submit: function (event) { event.preventdefault(); let formdata = new formdata(); formdata.append( "file" , this .file); axios.post( 'http://localhost:8088/upload' , formdata) .then(function (response) { for (let i = 0 ; i < response.data.length; i++) { var tempdata = { platform: response.data[i].platform, action: response.data[i].action, path: response.data[i].path, value: response.data[i].value, wait: response.data[i].wait, screenshot: response.data[i].screenshot }; this .steps.push(tempdata); } }.bind( this )) . catch (function (error) { alert( "fail" ); console.log(error); }); } |
9.運行服務,編寫script并上傳
接下來我們創建一個excel,按如圖格式編寫簡單script,運行前后端服務,實現上傳:
運行后,excel文件會上傳到后端工程的static的temp目錄中
總結
以上所述是小編給大家介紹的使用vue+spring boot實現excel上傳功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:https://juejin.im/post/5bff4a1851882516eb5625a1