上一篇完成了AdminLTE的整合,接下来就要把页面中的逻辑一一填充进来了,先从展示用户信息开始吧。
我们需要用户点击账户信息按钮后被导航到账户信息页。所以需要给账户信息按钮添加router-link,点击时调用router进行页面跳转。

第一步:在账户信息的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">职务:</b> <b class="pull-right ch">{{duty}}</b>
</li>
<li class="list-group-item">
<b class="ch">办公地点:</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">手机:</b> <b class="pull-right ch">{{phone}}</b>
</li>
<li class="list-group-item">
<b class="ch">上级领导:</b> <b class="pull-right ch">{{superior}}</b>
</li>
</ul>
<strong class="ch"><i class="fa fa-pencil margin-r-5"></i>技能标签</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>出差统计</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内容的地方-->
</div>
<!-- /.content -->
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {//这里对应用户的基本信息
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中。这个之后详细说明。
this.$http.get(
'https://192.168.227.1:8443/userInfo' ,
{
headers: {'token' : localStorage.token}//在requestHeader中携带之前产生的token用来在后端验证用户权限。
}
)
.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内容要显示的地方是在红框区域内,如下图:

而红框区域内的路由出口是在index.vue中定义的,所以如果想要userProfile的内容正确渲染到红框区域内,则需要把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提交方式设置
Vue.http.options.emulateJSON = true;
const routes = [
//登录页
{
path: '/login',
component : Login
},
//导航页
{
path: '/index',
component: Index,
//导航页子页面,children中的component将被渲染到之前说的红色区域内
children: [
//设备目录页
{
path: '/deviceCatalog',
component: DeviceCatalog
},
//账户信息页
{
path: '/userProfile',
component: UserProfile,
//账户信息子页面
children: [
//出差统计页
{
path: '/userProfile/travelCount',
component: TravelCount
},
//工作圈子页
{
path: '/userProfile/workCircle',
component: WorkCircle
}
]
},
]
},
]
const router = new VueRouter({
routes
})
//默认导航到登录页
// router.push('/login')
/*
全局路由钩子
访问资源时需要验证localStorage中是否存在token
以及token是否过期
验证成功可以继续跳转
失败返回登录页重新登录
*/
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
做完之后点击用户信息按钮应该就可以跳转到用户信息页了,看下效果

第四步出差统计信息,这里想使用echarts图表来进行展示(这里我跳了一个坑)。首先在项目中使用npm install echarts –save将echarts下载到项目中。然后在main.js中引用,对应第三步main.js中
import echarts from 'echarts'
在travelCount中使用,travelCount.vue。这里有几点需要注意
1.echarts挂载的dom必须设置一个高度,否则不能显示出来。
2.echarts的配置需要放在mounted中。
3.使用echarts的resize方法配合js中onresize重绘图表以动态适应屏幕尺寸。
<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老师指点的,谢谢老师)。
这种方法需要使用js的Object.defineProperties将echarts手动添加到Vue对象中,见第三步main.js配置。使用这种方法时不需要在根实例中注册echarts。
Object.defineProperties(Vue.prototype, {
$echarts: { get: () => echarts }
});
如果没有手动添加的话,在其他Vue文件中使用echarts时会报错,告诉你echarts没有定义过。
看下效果

OK,前端的部分完成了,现在出差信息是静态数据,因为来处理、记录这些数据的服务可能还要依赖其他服务,我们先用静态数据代替。但是用户基本信息是从Ldap中取出来的,之前第二步中不是使用了vur-resourse到这个地址获取用户数据么。
this.$http.get(
'https://192.168.227.1:8443/userInfo' ,
{
headers: {'token' : localStorage.token}
}
)
后端处理这个请求的controller。因为在分布式架构下这个controller通过restful对外提供获取用户信息的服务,所以需要使用@CrossOrigin注解满足跨域的需求。
getUserInfo方法做这几件事情
1.拿到header中的用户token,解密后判断用户凭证是否过期、是否拥有某种权限角色。
2.如果判断没有问题,使用token中的用户名作为入参,调用SpringLdap取得用户信息,并使用EmployeeAttributesMapper 填充我们自己的POJO类。返回response信息。
3.如果判断有问题,返回403表示认证没有通过。
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过期时间、用户权限
if(current > expiration || roles.indexOf("ROLE_USER") == -1) {
return new ResponseEntity<String>(HttpStatus.FORBIDDEN);
}
//查询并产生用户信息
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
写完收工,文字表达能力实在是差,各位费眼了。
|