上一篇完成了AdminLTE的整合,接下來就要把頁面中的邏輯一一填充進(jìn)來了,先從展示用戶信息開始吧。
我們需要用戶點(diǎn)擊賬戶信息按鈕后被導(dǎo)航到賬戶信息頁。所以需要給賬戶信息按鈕添加router-link,點(diǎn)擊時調(diào)用router進(jìn)行頁面跳轉(zhuǎn)。

第一步:在賬戶信息的HTML代碼處添加事件
<router-link to="/userProfile/travelCount">
<button href="#" class="btn btn-primary btn-flat ch">賬戶信息</button>
</router-link>
第二步:新建一個userProfile.vue和一個travelCount.vue。userProfile用來展示用戶的基本信息,travelCount用來展示用戶的出差記錄。travelCount是被嵌套在userProfile中的。
userProfile.vue:
<template>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-md-3">
<!-- Profile Image -->
<div class="box box-primary">
<div class="box-body box-profile">
<img class="profile-user-img img-responsive img-circle" src="../assets/img/avatar5.png" alt="User profile picture">
<h3 class="profile-username text-center ch">{{displayName}}</h3>
<p class="text-muted text-center ch">{{duty}}</p>
<ul class="list-group list-group-unbordered">
<li class="list-group-item">
<b class="ch">用戶名:</b> <b class="pull-right ch">{{displayName}}</b>
</li>
<li class="list-group-item">
<b class="ch">登錄名:</b> <b class="pull-right ch">{{name}}</b>
</li>
<li class="list-group-item">
<b class="ch">郵箱地址:</b> <b class="pull-right ch">{{email}}</b>
</li>
<li class="list-group-item">
<b class="ch">所屬部門:</b> <b class="pull-right ch">{{department}}</b>
</li>
<li class="list-group-item">
<b class="ch">職務(wù):</b> <b class="pull-right ch">{{duty}}</b>
</li>
<li class="list-group-item">
<b class="ch">辦公地點(diǎn):</b> <b class="pull-right ch">{{location}}</b>
</li>
<li class="list-group-item">
<b class="ch">辦公電話:</b> <b class="pull-right ch">{{tel}}</b>
</li>
<li class="list-group-item">
<b class="ch">手機(jī):</b> <b class="pull-right ch">{{phone}}</b>
</li>
<li class="list-group-item">
<b class="ch">上級領(lǐng)導(dǎo):</b> <b class="pull-right ch">{{superior}}</b>
</li>
</ul>
<strong class="ch"><i class="fa fa-pencil margin-r-5"></i>技能標(biāo)簽</strong>
<p style="padding-top:5px">
<button v-for="skill in skills" class="label btn-primary ch" style="margin:2px; color:white">{{skill}}</button>
</p>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
<div class="col-md-9">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="ch active" data-toggle="tab">
<router-link to="/userProfile/travelCount">
<span>出差統(tǒng)計</span>
</router-link>
</li>
<li class="ch" data-toggle="tab">
<router-link to="/userProfile/workCircle">
<span>我的工作圈</span>
</router-link>
</li>
</ul>
<div class="tab-content">
<router-view></router-view><!--這里是要顯示travelCount內(nèi)容的地方-->
</div>
<!-- /.content -->
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {//這里對應(yīng)用戶的基本信息
return {
displayName: null,
duty: null,
name: null,
email: null,
department: null,
location: null,
tel: null,
phone: null,
superior: null,
skills: null
}
},
mounted (){//使用mounted在掛在DOM時通過restful api獲取用戶基本信息并填充到data中。這個之后詳細(xì)說明。
this.$http.get(
'https://192.168.227.1:8443/userInfo' ,
{
headers: {'token' : localStorage.token}//在requestHeader中攜帶之前產(chǎn)生的token用來在后端驗證用戶權(quán)限。
}
)
.then(
//success
response => {
this.displayName = response.data.displayName;
this.duty = response.data.duty;
this.name = response.data.name;
this.email = response.data.email;
this.department = response.data.department;
this.location = response.data.location;
this.tel = response.data.tel;
this.phone = response.data.phone;
this.superior = response.data.superior;
this.skills = response.data.skills;
},
//error
response => {
}
)
}
}
</script>
<style scoped>
@font-face
{
font-family: yaHeiFont;
src: url('../assets/font/yaHei.ttf')
}
.ch
{
font-family:yaHeiFont;
color: black;
}
</style>
- 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
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 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
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
第三步,在main.js中引入userProfile.vue文件并且為userProfile添加路由。這里因為userProfile內(nèi)容要顯示的地方是在紅框區(qū)域內(nèi),如下圖:

而紅框區(qū)域內(nèi)的路由出口是在index.vue中定義的,所以如果想要userProfile的內(nèi)容正確渲染到紅框區(qū)域內(nèi),則需要把userProfile嵌套在index中,需要用到vue-router的嵌套路由。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import store from './store/store'
//bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap.min.js'
//AdminLTE
import './assets/css/skins/_all-skins.min.css'
import './assets/css/AdminLTE.min.css'
import './assets/js/app.min.js'
//font-awesome
import 'font-awesome/css/font-awesome.min.css'
//echarts
import echarts from 'echarts'
//components
import App from './App'
import Login from './components/login'
import Index from './components/index'
import DeviceCatalog from './components/deviceCatalog'
import UserProfile from './components/userProfile'
import TravelCount from './components/travelCount'
import WorkCircle from './components/workCircle'
Vue.use(VueRouter)
Vue.use(VueResource)
//注冊echarts的一種方法
// Object.defineProperties(Vue.prototype, {
// $echarts: { get: () => echarts }
// });
//Vue-Resource提交方式設(shè)置
Vue.http.options.emulateJSON = true;
const routes = [
//登錄頁
{
path: '/login',
component : Login
},
//導(dǎo)航頁
{
path: '/index',
component: Index,
//導(dǎo)航頁子頁面,children中的component將被渲染到之前說的紅色區(qū)域內(nèi)
children: [
//設(shè)備目錄頁
{
path: '/deviceCatalog',
component: DeviceCatalog
},
//賬戶信息頁
{
path: '/userProfile',
component: UserProfile,
//賬戶信息子頁面
children: [
//出差統(tǒng)計頁
{
path: '/userProfile/travelCount',
component: TravelCount
},
//工作圈子頁
{
path: '/userProfile/workCircle',
component: WorkCircle
}
]
},
]
},
]
const router = new VueRouter({
routes
})
//默認(rèn)導(dǎo)航到登錄頁
// router.push('/login')
/*
全局路由鉤子
訪問資源時需要驗證localStorage中是否存在token
以及token是否過期
驗證成功可以繼續(xù)跳轉(zhuǎn)
失敗返回登錄頁重新登錄
*/
router.beforeEach((to, from, next) => {
if(to.path == '/login'){
next()
}
if(localStorage.token && new Date().getTime() < localStorage.tokenExpired){
next()
}
else{
next('/login')
}
})
new Vue({
el: '#app',
template: '<App/>',
components: { App },
router:router,
store: store,
echarts: echarts//注冊echarts的另一種方法
}
})
- 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
- 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
做完之后點(diǎn)擊用戶信息按鈕應(yīng)該就可以跳轉(zhuǎn)到用戶信息頁了,看下效果

第四步出差統(tǒng)計信息,這里想使用echarts圖表來進(jìn)行展示(這里我跳了一個坑)。首先在項目中使用npm install echarts –save將echarts下載到項目中。然后在main.js中引用,對應(yīng)第三步main.js中
import echarts from 'echarts'
在travelCount中使用,travelCount.vue。這里有幾點(diǎn)需要注意
1.echarts掛載的dom必須設(shè)置一個高度,否則不能顯示出來。
2.echarts的配置需要放在mounted中。
3.使用echarts的resize方法配合js中onresize重繪圖表以動態(tài)適應(yīng)屏幕尺寸。
<template>
<div id="main" style="height:730px"></div>
</template>
<script>
export default {
mounted() {
var myChart = this.$root.$options.echarts.init(document.getElementById('main'));//這里注意
var option = {
grid : {
left : '1%',
right : '2%',
bottom : '1%',
containLabel : true
},
xAxis : {
type : 'value',
boundaryGap : [ 0, 0.01 ]
},
yAxis : {
type : 'category',
axisLabel : {
inside : true,
textStyle : {
fontWeight : 'bold'
},
},
z : 100,
data : ['A國:2016-01-01至2016-02-01' , 'B國:2016-03-01至2016-04-01' , 'C國:2016-06-01至2016-06-01']
},
series : [ {
type : 'bar',
barGap : '10%',
itemStyle : {
normal : {
color : 'LightSkyBlue'
}
},
data : [100 , 200 , 300]
} ]
}
myChart.setOption(option);
//窗口尺寸變化時重新繪制chart
window.onresize = () => {
myChart.resize()
}
}
}
</script>
- 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
- 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
這里要說下我跳的坑,引入echarts時,上面的代碼使用了
this.$root.$options.echarts
如果這樣引用的話,需要在main.js的Vue根實例中注冊echarts,見第三步main.js配置。還有一種方法可以使用this.$echarts的方式引用(這個方法是Vue論壇中tomi-li老師指點(diǎn)的,謝謝老師)。
這種方法需要使用js的Object.defineProperties將echarts手動添加到Vue對象中,見第三步main.js配置。使用這種方法時不需要在根實例中注冊echarts。
Object.defineProperties(Vue.prototype, {
$echarts: { get: () => echarts }
});
如果沒有手動添加的話,在其他Vue文件中使用echarts時會報錯,告訴你echarts沒有定義過。
看下效果

OK,前端的部分完成了,現(xiàn)在出差信息是靜態(tài)數(shù)據(jù),因為來處理、記錄這些數(shù)據(jù)的服務(wù)可能還要依賴其他服務(wù),我們先用靜態(tài)數(shù)據(jù)代替。但是用戶基本信息是從Ldap中取出來的,之前第二步中不是使用了vur-resourse到這個地址獲取用戶數(shù)據(jù)么。
this.$http.get(
'https://192.168.227.1:8443/userInfo' ,
{
headers: {'token' : localStorage.token}
}
)
后端處理這個請求的controller。因為在分布式架構(gòu)下這個controller通過restful對外提供獲取用戶信息的服務(wù),所以需要使用@CrossOrigin注解滿足跨域的需求。
getUserInfo方法做這幾件事情
1.拿到header中的用戶token,解密后判斷用戶憑證是否過期、是否擁有某種權(quán)限角色。
2.如果判斷沒有問題,使用token中的用戶名作為入?yún)?,調(diào)用SpringLdap取得用戶信息,并使用EmployeeAttributesMapper 填充我們自己的POJO類。返回response信息。
3.如果判斷有問題,返回403表示認(rèn)證沒有通過。
package an.userinfo;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.Date;
import javax.naming.directory.Attributes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import an.entity.Employee;
import io.jsonwebtoken.Jwts;
@RestController("/userInfo")
public class UserInfoWeb {
//jwt加密密匙
@Value("${jwt.key}")
private String jwtKey;
//ldap模板
@Autowired
private LdapTemplate ldapTemplate;
/**
* 將域用戶屬性通過EmployeeAttributesMapper填充到Employee類中,返回一個填充信息的Employee實例
*/
private class EmployeeAttributesMapper implements AttributesMapper<Employee> {
public Employee mapFromAttributes(Attributes attrs) throws NamingException, javax.naming.NamingException {
Employee employee = new Employee();
employee.setName((String) attrs.get("sAMAccountName").get());
employee.setDisplayName((String) attrs.get("displayName").get());
employee.setEmail((String) attrs.get("userprincipalname").get());
employee.setDuty((String) attrs.get("title").get());
employee.setDepartment((String) attrs.get("department").get());
employee.setSuperior((String) attrs.get("manager").get());
employee.setLocation((String) attrs.get("physicaldeliveryofficename").get());
employee.setTel((String) attrs.get("homephone").get());
employee.setPhone((String) attrs.get("mobile").get());
String skill = (String) attrs.get("description").get();
String skills[] = skill.split(";");
employee.setSkills(skills);
return employee;
}
}
/**
* 使用用戶憑證取得用戶信息
* @param token 用戶憑證
* @return
*/
@CrossOrigin
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> getUserInfo(@RequestHeader("token") String token){
//解析token
String username = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getSubject();
String roles = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getAudience();
long expiration = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getExpiration().getTime();
long current = new Date().getTime();
//驗證token過期時間、用戶權(quán)限
if(current > expiration || roles.indexOf("ROLE_USER") == -1) {
return new ResponseEntity<String>(HttpStatus.FORBIDDEN);
}
//查詢并產(chǎn)生用戶信息
Employee employee = ldapTemplate
.search(query().where("objectclass").is("person").and("sAMAccountName").is(username),
new EmployeeAttributesMapper())
.get(0);
return new ResponseEntity<String>(JSON.toJSONString(employee , SerializerFeature.DisableCircularReferenceDetect) , HttpStatus.OK);
}
}
- 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
- 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
寫完收工,文字表達(dá)能力實在是差,各位費(fèi)眼了。
|