回文集目录:JHipster一知半解
spring-boot的一大优势就是提供了Actuator 监控端点,便于对服务运行状态进行感知,而不是需要通过命令行,查看日志等辅助。但是这些都是json形式的数据,并不是很直观。JHipster就提供了通用的admin界面,把暴露的Actuator监控端点数据图文化,转换更适合人类阅读的方式。
除此spring-boot-actuator集成进来的dropwiard的metrics,health监控、logback实时日志级别改变(spring-boot目前也内嵌了这个功能),swagger-api,H2数据库控制、以及自己实现的用户管理、操作审计等都整合在一起,放到Management模块,给用户提供开箱即用的管理全家桶。
这个模块十分强大,也是Jhipster与其他框架对比时可圈可点的一大亮点功能。
admin.module.ts
@NgModule({
imports: [
//引入共享模块和初始化子路由
JhipsterSampleApplicationNg2SharedModule,
RouterModule.forChild(adminState),
/* jhipster-needle-add-admin-module - JHipster will add admin modules here */
],
//声明内部组件,可以看出,还是比较丰富的
declarations: [
AuditsComponent,
UserMgmtComponent,
UserDialogComponent,
UserDeleteDialogComponent,
UserMgmtDetailComponent,
UserMgmtDialogComponent,
UserMgmtDeleteDialogComponent,
LogsComponent,
JhiConfigurationComponent,
JhiHealthCheckComponent,
JhiHealthModalComponent,
JhiDocsComponent,
JhiMetricsMonitoringComponent,
JhiMetricsMonitoringModalComponent
],
//声明入口组件
entryComponents: [
UserMgmtDialogComponent,
UserMgmtDeleteDialogComponent,
JhiHealthModalComponent,
JhiMetricsMonitoringModalComponent,
],
//声明通讯服务
providers: [
AuditsService,
JhiConfigurationService,
JhiHealthService,
JhiMetricsService,
LogsService,
UserResolvePagingParams,
UserResolve,
UserModalService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class JhipsterSampleApplicationNg2AdminModule {}
路由配置
admin.route.ts
export const adminState: Routes = [{
path: '',
data: {
//在子路由的顶层增加ROLE_ADMIN的权限要求
authorities: ['ROLE_ADMIN']
},
//有权限控制,就必须有路由守卫
canActivate: [UserRouteAccessService],
children: ADMIN_ROUTES
},
//用户编辑对话框是单独的路由,
...userDialogRoute
];
user-management.route.ts
userDialogRoute需要单独配置,它也是AdminModule的子路由,但是其<outlet>是在popup上的,也就是说,相当于JHipster实现的一个弹出层。
export const userDialogRoute: Routes = [
{
path: 'user-management-new',
component: UserDialogComponent,
outlet: 'popup'
},
{
path: 'user-management/:login/edit',
component: UserDialogComponent,
outlet: 'popup'
},
{
path: 'user-management/:login/delete',
component: UserDeleteDialogComponent,
outlet: 'popup'
}
];
audits目录
审计信息,用户使用系统的记录。
audit-data.model.ts和audit.model.ts
表示服务器返回的审计内容模型,与org.springframework.boot.actuate.audit.AuditEvent对应。
//API通讯,构造查询Pageable和fromDate、toDate参数
//后端@GetMapping("/{id:.+}")这种,这里就没有用了。
query(req: any): Observable<Response> {
const params: URLSearchParams = new URLSearchParams();
params.set('fromDate', req.fromDate);
params.set('toDate', req.toDate);
params.set('page', req.page);
params.set('size', req.size);
params.set('sort', req.sort);
//根据RequestOptionsArgs定义,在search参数传递查询里面,会自己在url生成查询串
//例如:http://localhost:9060/management/audits?fromDate=2017-12-13&toDate=2018-01-14&page=0&size=20,但是再angular的ts文件中,可发现
/** @deprecated from 4.0.0. Use params instead. */
//也就是说现在应该改成params: params
const options = {
search: params
};
return this.http.get(SERVER_API_URL + 'management/audits', options);
}
audits.component.ts
由于使用了日期控件,所以还是值得一看的。在ngOnInit()中就初始化结束时间为明天,而开始时间为前一个月,然后开始调用onChangeDate()查询。
onChangeDate()是实际与后端通讯的地方,调用了auditsService的query(),根据返回结果更新页面内容。其中在报文头“X-Total-Count”会返回查询的总数目,有利于分页,而Pageable不用放这些信息。
audits.component.html
<p class="d-flex">
<span jhiTranslate="audits.filter.from" class="input-group-addon">from</span>
<!-- 直接用的是原生date型的input,所以不是很漂亮 -->
<!-- 每次值变动都会调用onChangeDate,重新获取数据 -->
<input type="date" class="form-control" name="start" [(ngModel)]="fromDate" (ngModelChange)="onChangeDate($event)" required/>
<span jhiTranslate="audits.filter.to" class="input-group-addon">to</span>
<input type="date" class="form-control" name="end" [(ngModel)]="toDate" (ngModelChange)="onChangeDate($event)" required/>
</p>
<!--循环显示查询的Audits数据,利用插值显示 -->
<tr *ngFor="let audit of getAudits()">
<td><span>{{audit.timestamp| date:'medium'}}</span></td>
<td><small>{{audit.principal}}</small></td>
<td>{{audit.type}}</td>
<!-- audit.data数据放在一起,用ng-show显示 -->
<td>
<span *ngIf="audit.data" ng-show="audit.data.message">{{audit.data.message}}</span>
<span *ngIf="audit.data" ng-show="audit.data.remoteAddress"><span jhiTranslate="audits.table.data.remoteAddress">Remote Address</span> {{audit.data.remoteAddress}}</span>
</td>
</tr>
configuration目录
configuration.service
与spring-boot-actuator提供/configprops,/env通讯,获取配置properties信息。
configuration.component.ts
在init是加载所有的属性
ngOnInit() {
//获取configprops所有配置,并保存其key值
this.configurationService.get().subscribe((configuration) => {
this.configuration = configuration;
for (const config of configuration) {
if (config.properties !== undefined) {
this.configKeys.push(Object.keys(config.properties));
}
}
});
//获取/env所有配置,叫allConfiguration,不是应该叫envConfiguration吗?
this.configurationService.getEnv().subscribe((configuration) => {
this.allConfiguration = configuration;
});
}
configuration.component.html
<div *ngIf="allConfiguration && configuration">
<h2 jhiTranslate="configuration.title">Configuration</h2>
<!--用ngModel绑定一个过滤输入框-->
<span jhiTranslate="configuration.filter">Filter (by prefix)</span> <input type="text" [(ngModel)]="filter" class="form-control">
<!--hard code,似乎没必要翻译-->
<label>Spring configuration</label>
<table class="table table-striped table-bordered table-responsive d-table">
<thead>
<tr>
<th class="w-40" (click)="orderProp = 'prefix'; reverse=!reverse"><span jhiTranslate="configuration.table.prefix">Prefix</span></th>
<th class="w-60" (click)="orderProp = 'properties'; reverse=!reverse"><span jhiTranslate="configuration.table.properties">Properties</span></th>
</tr>
</thead>
<tbody>
<!--这里使用了自定义的pureFilter根据上面的过滤输入进行过滤(仅之前前向过滤)-->
<!--然后在用排序管道按照'prefix'的顺序排序-->
<tr *ngFor="let entry of (configuration | pureFilter:filter:'prefix' | orderBy:orderProp:reverse)">
<td><span>{{entry.prefix}}</span></td>
<td>
<div class="row" *ngFor="let key of keys(entry.properties)">
<div class="col-md-4">{{key}}</div>
<div class="col-md-8">
<span class="float-right badge badge-secondary break">{{entry.properties[key] | json}}</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<!--env部分,就是完全显示出来-->
<div *ngFor="let key of keys(allConfiguration)">
<label><span>{{key}}</span></label>
<table class="table table-sm table-striped table-bordered table-responsive d-table">
<thead>
<tr>
<th class="w-40">Property</th>
<th class="w-60">Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of allConfiguration[key]">
<td class="break">{{item.key}}</td>
<td class="break">
<span class="float-right badge badge-secondary break">{{item.val}}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
利用JHipster实现的管道,提供了一个前端的过滤Filter功能。
网友评论