diff options
Diffstat (limited to 'usecaseui-portal/src/app')
85 files changed, 9046 insertions, 0 deletions
diff --git a/usecaseui-portal/src/app/alarm/alarm.component.css b/usecaseui-portal/src/app/alarm/alarm.component.css new file mode 100644 index 00000000..757a55b6 --- /dev/null +++ b/usecaseui-portal/src/app/alarm/alarm.component.css @@ -0,0 +1,138 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select .query_criteria span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; +} +.select .query_criteria nz-dropdown { + vertical-align: middle; +} +.select .query_criteria nz-dropdown :hover { + border-color: #147dc2; +} +.select .query_criteria nz-dropdown button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; +} +.select .query_criteria nz-dropdown button span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; +} +.select .query_criteria nz-dropdown button i { + position: absolute; + top: 10px; + right: 10px; +} +.select .query_criteria .search { + margin-left: 20px; + height: 30px; + padding: 0 10px; +} +.select .query_criteria .search span { + color: #fff; + font-weight: 400; +} +::ng-deep .vertical-center-modal { + display: flex; + align-items: center; + justify-content: center; +} +::ng-deep .vertical-center-modal .ant-modal { + top: 0; +} +.content { + clear: both; + padding-top: 20px; +} +.content .title { + border-radius: 5px 5px 5px 5px; + background-color: #fff; + height: 106px; + border-bottom: 1px solid #f0f0f0; + margin-bottom: 20px; + clear: both; +} +.content .title ul { + display: flex; + display: -webkit-flex; + justify-content: space-around; + align-items: center; + padding: 0; + margin: 0; + height: 100%; + width: 70%; + float: left; +} +.content .title ul li { + list-style: none; + padding-left: 32px; + width: 100%; + border-left: 1px solid #eceff4; +} +.content .title ul li h5 { + font: 500 14px "Arial"; + color: #3d4d65; +} +.content .title ul li p { + font: 500 24px "Arial"; + color: #3fa8eb; + margin-bottom: 0; +} +.content .title ul li:nth-child(1) { + border: none; +} +.content .title .thumbnail { + width: 25%; + height: 90px; + float: left; + margin-top: 8px; +} +.content .tablelist { + background-color: #fff; + padding: 24px 10px 0px; + border-radius: 0 0 5px 5px; +} +.content .tablelist .action { + padding: 10px 0 0 20px; +} +.content .tablelist .action .details { + display: inline-block; + width: 16px; + height: 16px; + background: url(../../assets/images/icon.png) center -113px; +} +.content .tablelist .action .details:hover { + background: url(../../assets/images/icon.png) no-repeat center -128px; +} diff --git a/usecaseui-portal/src/app/alarm/alarm.component.html b/usecaseui-portal/src/app/alarm/alarm.component.html new file mode 100644 index 00000000..8f95de37 --- /dev/null +++ b/usecaseui-portal/src/app/alarm/alarm.component.html @@ -0,0 +1,113 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> <span (click)="detailHide()" style="cursor:pointer;">Alarm</span> <span *ngIf="detailshow">> + Details</span> </h3> +<hr> +<div class="select" [@showHideAnimate]="state"> + <div class="query_criteria"> + <span>Source Name: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{sourceNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseSourceName(item)" *ngFor="let item of sourceNameList"> + <a>{{item.name}}</a> + </li> + </ul> + </nz-dropdown> + + <span>Priority: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{prioritySelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="chosePriority(item)" *ngFor="let item of priorityList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + + <span>Status: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{statusSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseStatus(item)" *ngFor="let item of statusList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + <br> <br> + <span>Report Time: </span> + <nz-range-picker [(ngModel)]="dateRange" (ngModelChange)="onChange($event)" nzShowTime></nz-range-picker> + + <button class="search" nz-button [nzType]="'primary'" (click)="getAlarmFormData()"><i class="anticon anticon-search"></i><span>Search</span></button> + </div> +</div> +<nz-modal nzWrapClassName="vertical-center-modal" [(nzVisible)]="isVisibleMiddle" nzTitle="Alarm Chart" (nzOnCancel)="handleCancelMiddle()" + (nzOnOk)="handleOkMiddle()"> + <app-line [initData]="alarmChartInitBig" [chartData]="alarmChartDataBig"></app-line> +</nz-modal> +<div class="content" [@showHideAnimate]="state"> + <div class="title"> + <ul> + <li> + <h5>All</h5> + <p>{{alarmList.all }}</p> + </li> + <li> + <h5>Closed</h5> + <p>{{alarmList.closed }}</p> + </li> + <li> + <h5>Action</h5> + <p>{{alarmList.Action }}</p> + </li> + </ul> + <div class="thumbnail" (click)="showModalMiddle()"> + <app-line [initData]="alarmChartInit" [chartData]="alarmChartData"></app-line> + </div> + </div> + <div class="tablelist"> + <nz-table #nzTable [nzData]="dataSet" [nzPageSize]="10" nzShowSizeChanger nzShowQuickJumper [nzPageSizeOptions]="[5,10,15,20]" + nzSize="middle"> + <thead (nzSortChange)="sort($event)" nzSingleSort> + <tr> + <th nzWidth="5%">NO</th> + <th nzWidth="20%">Source Name</th> + <th nzWidth="10%">Priority</th> + <th nzWidth="20%">SpecificProblem</th> + <th nzWidth="20%">Report Time</th> + <th nzWidth="15%">Status</th> + <th nzWidth="10%">Action</th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of list; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.age}}</td> + <td>{{item.address}}</td> + <td>{{item.address}}</td> + <td>{{item.address}}</td> + <td class="action"><a (click)="detailShow()"><i class="details"></i></a></td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> +</div> +<div [@showHideAnimate]="state2"> + <app-details></app-details> +</div>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/alarm/alarm.component.less b/usecaseui-portal/src/app/alarm/alarm.component.less new file mode 100644 index 00000000..2d05c703 --- /dev/null +++ b/usecaseui-portal/src/app/alarm/alarm.component.less @@ -0,0 +1,132 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select { + .query_criteria { + span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; + } + nz-dropdown { + vertical-align: middle; + :hover{ + border-color: #147dc2; + } + button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; + span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; + } + i { + position: absolute; + top: 10px; + right: 10px; + } + } + //下拉框中的样式在style.less中,下拉框是在body中额外临时生成的 + } + .search { + margin-left: 20px; + height: 30px; + padding: 0 10px; + span { + color: #fff; + font-weight: 400; + } + } + } + +} +// model style + +::ng-deep .vertical-center-modal { + display: flex; + align-items: center; + justify-content: center; +} +::ng-deep .vertical-center-modal .ant-modal { + top: 0; +} + +.content { + clear: both; + padding-top: 20px; + .title { + border-radius: 5px 5px 5px 5px; + background-color: #fff; + height: 106px; + border-bottom: 1px solid #f0f0f0; + margin-bottom: 20px; + clear: both; + ul { + display: flex; + display: -webkit-flex; + justify-content: space-around; + align-items: center; + padding: 0; + margin: 0; + height: 100%; + width: 70%; + float: left; + li { + list-style: none; + padding-left: 32px; + width: 100%; + border-left: 1px solid #eceff4; + h5 { + font: 500 14px "Arial"; + color: #3d4d65; + } + p { + font: 500 24px "Arial"; + color: #3fa8eb; + margin-bottom: 0; + } + } + li:nth-child(1){ + border: none; + } + } + .thumbnail { + width: 25%; + height: 90px; + float: left; + margin-top: 8px; + } + } + .tablelist { + background-color: #fff; + padding: 24px 10px 0px; + border-radius: 0 0 5px 5px; + .action{ + padding: 10px 0 0 20px; + .details{ + display: inline-block; + width: 16px; + height: 16px; + background: url(../../assets/images/icon.png) center -113px; + &:hover { + background: url(../../assets/images/icon.png) no-repeat center -128px; + } + } + } + } +} diff --git a/usecaseui-portal/src/app/alarm/alarm.component.spec.ts b/usecaseui-portal/src/app/alarm/alarm.component.spec.ts new file mode 100644 index 00000000..c7f5a738 --- /dev/null +++ b/usecaseui-portal/src/app/alarm/alarm.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AlarmComponent } from './alarm.component'; + +describe('AlarmComponent', () => { + let component: AlarmComponent; + let fixture: ComponentFixture<AlarmComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AlarmComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AlarmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/alarm/alarm.component.ts b/usecaseui-portal/src/app/alarm/alarm.component.ts new file mode 100644 index 00000000..72dd5181 --- /dev/null +++ b/usecaseui-portal/src/app/alarm/alarm.component.ts @@ -0,0 +1,380 @@ +import { Component, OnInit, Input, Output, EventEmitter, HostBinding } from '@angular/core'; +import { MyhttpService } from '../myhttp.service'; +import * as addDays from 'date-fns/add_days'; +import { showHideAnimate, slideToRight } from '../animates'; + +@Component({ + selector: 'app-alarm', + templateUrl: './alarm.component.html', + styleUrls: ['./alarm.component.less'], + animations: [ + showHideAnimate, slideToRight + ] +}) +export class AlarmComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; //路由动画 + public pageNumber:number=1; + public pageSize:number=5; + public name:string=''; + public Priority:string =''; + public Status:string =''; + public Report:string =''; + list: any; + + + constructor( + private myhttp:MyhttpService) { } + isVisibleMiddle = false; + + showModalMiddle(): void { + this.isVisibleMiddle = true; + } + handleOkMiddle(): void { + console.log('click ok'); + this.isVisibleMiddle = false; + } + handleCancelMiddle(): void { + this.isVisibleMiddle = false; + } + ngOnInit() { + this.getAlarmFormData(); + } + + // 筛选框(下拉框) + // sourceNameList = ['aaaa','bbbb','cccc','dddddDDDDDDDDDDDDDDD']; + sourceNameList=[ + {key:null,name:'请选择'}, + {key:1,name:'aaaa'}, + {key:2,name:'bbbb'}, + {key:3,name:'cccc'}, + {key:4,name:'dddddDDDDDDDDDDDDDDD'} + ] + sourceNameSelected = this.sourceNameList[0].name; + priorityList = ['aaaa','bbbb','cccc','ddddd']; + prioritySelected = this.priorityList[0]; + statusList = ['aaaa','bbbb','cccc','ddddd']; + statusSelected = this.statusList[0]; + choseSourceName(item){ + console.log(item,'item1'); + this.sourceNameSelected = item; + } + chosePriority(item){ + console.log(item); + this.prioritySelected = item; + } + choseStatus(item){ + console.log(item); + this.statusSelected = item; + } + + // 日期筛选 + dateRange = [ addDays(new Date(), -30), new Date() ]; + + onChange(result: Date): void { + console.log('onChange: ', result); + } + // search(){ + // this.myhttp.getAlarmFormData().subscribe + + // } + sort(e){ + + } + // 数量统计 + alarmList = { + all:22439, + closed:37923, + Action: 12342 + } + + //折线图縮略圖 + alarmChartData:Object; + alarmChartInit:Object = { + height:100, + width:290, + option:{ + tooltip : { + show : false, + trigger: 'axis', + }, + legend: { + show :false, + bottom: '0px', + data: ['All', 'Active', 'Closed'] + }, + series: [ + { + name: 'All', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_one + data: [40, 45, 38, 52, 64, 58, 69, 87, 76, 33, 64, 87, 45, 76, 88, 56, 33, 76, 45, 65], + itemStyle: { + color: "#526b75" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + }, + { + name: 'Active', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_two + data: [32, 43, 23, 45, 63, 24, 54, 22, 32, 42, 42, 22, 23, 43, 32, 34, 42, 33, 42, 12], + itemStyle: { + color: "#fb6e6e" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + }, + { + name: 'Closed', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_two + data: [12, 23, 13, 25, 33, 14, 34, 12, 12, 22, 12, 12, 13, 23, 12, 24, 22, 13, 22, 5], + itemStyle: { + color: "#3fa8eb" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + } + ] + } + }; + getAlarmChartData(){ + let paramsObj = { + alarmSourceName:this.sourceNameSelected + } + this.myhttp.getHomePerformanceChartData(paramsObj) + .subscribe((data)=>{ + this.alarmChartData = { + series:[ + {data:data.CPU}, + {data:data.CPU}, + {data:data.Memory} + ] + } + },(err)=>{ + console.log(err); + }) + } + //折线图放大圖 + alarmChartDataBig:Object; + alarmChartInitBig:Object = { + height:240, + width:500, + option:{ + tooltip : { + show : true, + trigger: 'axis', + }, + legend: { + show :true, + bottom: '0px', + data: ['All', 'Active', 'Closed'] + }, + series: [ + { + name: 'All', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_one + data: [40, 45, 38, 52, 64, 58, 69, 87, 76, 33, 64, 87, 45, 76, 88, 56, 33, 76, 45, 65], + itemStyle: { + color: "#526b75" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + }, + { + name: 'Active', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_two + data: [32, 43, 23, 45, 63, 24, 54, 22, 32, 42, 42, 22, 23, 43, 32, 34, 42, 33, 42, 12], + itemStyle: { + color: "#fb6e6e" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + }, + { + name: 'Closed', + type: 'line', + smooth: true,//将图变得平缓 + showSymbol: false, + areaStyle: { + opacity: 0.8 + }, + //timeframe_two + data: [12, 23, 13, 25, 33, 14, 34, 12, 12, 22, 12, 12, 13, 23, 12, 24, 22, 13, 22, 5], + itemStyle: { + color: "#3fa8eb" + }, + lineStyle: { + width: 1, + opacity: 0.5 + } + } + ] + } + }; + //表格数据 + dataSet = [ + { + name : 'John Brown', + age : 32, + expand : false, + address : 'New York No. 1', + description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.' + }, + { + name : 'Aim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Xim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'aoe Black', + age : 32, + expand : false, + address : 'Sidney No. 1', + description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.' + } + ]; + + //详情页标题显示 + detailshow = false; + // 显示隐藏动画 + state = "show"; + state2 = "hide"; + detailShow() { + this.state = 'hide'; + this.state2 = 'show'; + this.detailshow = true; + } + detailHide() { + this.state = 'show'; + this.state2 = 'hide'; + this.detailshow = false; + } + getSelects:Object = { + countAll:0, + countClose:0, + countActive:0, + eventNameList:[], + sourceIdList:[], + reportingEntityNameList:[], + sourceNameList:[], +}; + getAlarmFormData(){ + this.myhttp.getAlarmFormData(this.pageNumber,this.pageSize,this.name,this.Priority,this.Status,this.Report).subscribe((data)=>{ + if(data.retCode ==200){ + this.list = data.list; + } + console.log(data,'data'); + }) + } +} diff --git a/usecaseui-portal/src/app/animates.ts b/usecaseui-portal/src/app/animates.ts new file mode 100644 index 00000000..cde7e568 --- /dev/null +++ b/usecaseui-portal/src/app/animates.ts @@ -0,0 +1,42 @@ +import { trigger, state, style, animate, transition } from '@angular/animations'; + +// 路由动画 +export const slideToRight = trigger('routerAnimate', [ + // 定义void表示空状态下 + state('void', style({ position:'fixed', zIndex:'-1' })), //不明白为啥要加定位出场动画才生效 + // * 表示任何状态 + state('*', style({ })), + // 进场动画 + transition(':enter', [ + style({transform: 'translateX(-100%)'}), + animate('.5s ease-in-out') + ]), + // 出场动画 + transition(':leave', [ + animate('.5s ease-in-out', style({transform: 'translateX(100%)'}) ) + ]) +]); +// 详情页显示隐藏动画 +export const showHideAnimate = trigger('showHideAnimate', [ + state('show', style({ + transform: 'scale(1)', + display:'block', + })), + state('hide', style({ + transform: 'scale(0)', + display:'none' + })), + transition('show => hide', animate('300ms ease-in')), + transition('hide => show', animate('300ms ease-out')) +]); +// 详情页显示隐藏动画 +export const slideUpDown = trigger('slideUpDown', [ + state('down', style({ + height: "*" + })), + state('up', style({ + height: "0" + })), + transition('down => up', animate('300ms ease-in')), + transition('up => down', animate('300ms ease-out')) +]);
\ No newline at end of file diff --git a/usecaseui-portal/src/app/app-routing.module.ts b/usecaseui-portal/src/app/app-routing.module.ts new file mode 100644 index 00000000..7fe7361c --- /dev/null +++ b/usecaseui-portal/src/app/app-routing.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + + +import { HomeComponent } from './home/home.component'; +import { ServicesComponent } from './services/services.component'; +import { ServicesListComponent } from './services/services-list/services-list.component'; +import { OnboardVnfVmComponent } from './services/onboard-vnf-vm/onboard-vnf-vm.component'; +import { AlarmComponent } from './alarm/alarm.component'; +import { PerformanceComponent } from './performance/performance.component'; +import { PerformanceVnfComponent } from './performance/performance-vnf/performance-vnf.component'; +import { PerformanceVmComponent } from './performance/performance-vm/performance-vm.component'; + +import { CcvpnNetworkComponent } from './ccvpn-network/ccvpn-network.component'; + +// import { DetailsComponent } from './details/details.component'; + +const ServicesChildRoutes: Routes = [ + { path: 'services-list', component: ServicesListComponent}, + { path: 'onboard-vnf-vm', component: OnboardVnfVmComponent}, + { path: '**', redirectTo: 'services-list' } +] + +const routes: Routes = [ + { path: 'home', component: HomeComponent}, + // { path: 'services', component: ServicesComponent, children:ServicesChildRoutes}, //暂时不是子路由结构 + { path: 'services/services-list', component: ServicesListComponent}, + { path: 'services/onboard-vnf-vm', component: OnboardVnfVmComponent}, + { path: 'alarm', component: AlarmComponent}, + { path: 'performance', component: PerformanceComponent}, + { path: 'performance/performance-vnf', component: PerformanceVnfComponent}, + { path: 'performance/performance-vm', component: PerformanceVmComponent}, + { path: 'network', component: CcvpnNetworkComponent }, + { path: '**', redirectTo: 'home', pathMatch: 'full'} +]; + +@NgModule({ + imports: [ RouterModule.forRoot(routes) ], + exports: [ RouterModule ] +}) +export class AppRoutingModule {} diff --git a/usecaseui-portal/src/app/app.component.css b/usecaseui-portal/src/app/app.component.css new file mode 100644 index 00000000..f25d3262 --- /dev/null +++ b/usecaseui-portal/src/app/app.component.css @@ -0,0 +1,39 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +nz-layout nz-sider ul li { + margin: 0; + font-size: 16px; +} +nz-layout nz-sider ul li span { + font-size: 16px; +} +nz-layout nz-sider ul li i { + width: 16px; + height: 16px; +} +nz-layout nz-sider ul li .icon-services { + background: url(../assets/images/icon.png) no-repeat 0px -16px; +} +nz-layout nz-sider ul li ul li { + font-size: 12px; +} +nz-layout nz-sider ul hr { + margin: 0 auto; + background-color: #39434f; + height: 1px; + border: none; + width: 80%; +} diff --git a/usecaseui-portal/src/app/app.component.html b/usecaseui-portal/src/app/app.component.html new file mode 100644 index 00000000..8e189e73 --- /dev/null +++ b/usecaseui-portal/src/app/app.component.html @@ -0,0 +1,80 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<nz-layout> + <nz-sider nzWidth='260' style="overflow: auto; height: 100vh; position: fixed; left: 0"> + <ul nz-menu [nzTheme]="'dark'" [nzMode]="'inline'" style="width: 260px;"> + <li nz-menu-item nzSelected> + <a routerLink="home"> + <span title> + <i class="anticon anticon-home"></i> + <span> {{"Home" | translate}} </span> + </span> + </a> + </li> + <hr> + <li nz-submenu> + <span title><i class="anticon anticon-home"></i> {{"Services" | translate}} </span> + <ul> + <li nz-menu-item><a routerLink='services/services-list'> {{"Services List" | translate}}</a></li> + <li nz-menu-item><a routerLink='services/onboard-vnf-vm'> Onboard VNF/VM </a></li> + </ul> + </li> + <!-- <hr> + <li nz-menu-item> + <a routerLink="alarm"> + <span title> + <i class="anticon anticon-mail"></i> + <span> {{"Alarm" | translate}} </span> + </span> + </a> + </li> + <hr> --> + <li nz-submenu> + <span title><i class="anticon anticon-setting"></i> {{"Monitor" | translate}} </span> + <ul> + <li nz-menu-item><a routerLink='alarm'> {{"Alarm" | translate}} </a></li> + <li nz-menu-item><a routerLink='performance/performance-vnf'> {{"Performance" | translate}}</a></li> + </ul> + </li> + <hr> + <li nz-menu-item> + <a routerLink="network"> + <span title> + <i class="anticon anticon-share-alt"></i> + <span> {{"Network" | translate}} </span> + </span> + </a> + </li> + </ul> + <button (click)="changeLanguage('zh')" >{{"zh" | translate}}</button> + <button (click)="changeLanguage('en')" >{{"en" | translate}}</button> + <select name="Language" id="" [(ngModel)]="selectLanguage" (change)="changeLanguage1()"> + <option *ngFor="let item of Language" value="{{item}}">{{item | translate}}</option> + </select> + + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomRight'"> + <button nz-button nz-dropdown><span>{{selectLanguage | translate}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="changeLanguage2(item)" *ngFor="let item of Language"> + <a>{{item | translate}}</a> + </li> + </ul> + </nz-dropdown> + </nz-sider> + <nz-layout style="margin-left: 260px; padding: 20px 32px; height:100vh; position:relative;"> + <router-outlet></router-outlet> + </nz-layout> +</nz-layout> diff --git a/usecaseui-portal/src/app/app.component.less b/usecaseui-portal/src/app/app.component.less new file mode 100644 index 00000000..a805166e --- /dev/null +++ b/usecaseui-portal/src/app/app.component.less @@ -0,0 +1,32 @@ +nz-layout { + nz-sider { + ul { + li { + margin: 0; + font-size: 16px; + span { + font-size: 16px; + } + i { + width: 16px; + height: 16px; + } + .icon-services { + background: url(../assets/images/icon.png) no-repeat 0px -16px; + } + ul { + li{ + font-size: 12px; + } + } + } + hr { + margin: 0 auto; + background-color: #39434f; + height: 1px; + border: none; + width: 80%; + } + } + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/app.component.spec.ts b/usecaseui-portal/src/app/app.component.spec.ts new file mode 100644 index 00000000..bcbdf36b --- /dev/null +++ b/usecaseui-portal/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + it('should create the app', async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + })); + it(`should have as title 'app'`, async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('app'); + })); + it('should render title in a h1 tag', async(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); + })); +}); diff --git a/usecaseui-portal/src/app/app.component.ts b/usecaseui-portal/src/app/app.component.ts new file mode 100644 index 00000000..be13c1a1 --- /dev/null +++ b/usecaseui-portal/src/app/app.component.ts @@ -0,0 +1,34 @@ +import { Component } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.less'] +}) +export class AppComponent { + constructor(private translate:TranslateService){ + translate.addLangs(['en', 'zh']); + translate.setDefaultLang('en'); + // translate.use('en'); + } + changeLanguage(Language): void { + switch(Language){ + case 'en': + this.translate.use('en'); + break; + case 'zh': + this.translate.use('zh'); + } + } + // 多语言 + Language:String[] = ["zh","en"]; + selectLanguage = "en"; + changeLanguage1(){ + this.translate.use(this.selectLanguage); + } + changeLanguage2(item){ + this.translate.use(item); + } +} diff --git a/usecaseui-portal/src/app/app.module.ts b/usecaseui-portal/src/app/app.module.ts new file mode 100644 index 00000000..7c24f110 --- /dev/null +++ b/usecaseui-portal/src/app/app.module.ts @@ -0,0 +1,98 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; +import { HttpClient } from '@angular/common/http'; +import { NgZorroAntdModule } from 'ng-zorro-antd'; +import { NZ_I18N, en_US } from 'ng-zorro-antd'; +import { NgxEchartsModule } from 'ngx-echarts'; + +import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; +export function HttpLoaderFactory(httpClient: HttpClient) { + return new TranslateHttpLoader(httpClient, '../assets/i18n/', '.json'); +} + +import { AppRoutingModule } from './app-routing.module'; + +//注册语言包 +import { registerLocaleData } from '@angular/common'; +import en from '@angular/common/locales/en'; +registerLocaleData(en); + +//自定义组件 +import { AppComponent } from './app.component'; +import { HomeComponent } from './home/home.component'; +import { ServicesComponent } from './services/services.component'; +import { ServicesListComponent } from './services/services-list/services-list.component'; +import { OnboardVnfVmComponent } from './services/onboard-vnf-vm/onboard-vnf-vm.component'; +import { AlarmComponent } from './alarm/alarm.component'; +import { PerformanceComponent } from './performance/performance.component'; +import { PerformanceVnfComponent } from './performance/performance-vnf/performance-vnf.component'; +import { PerformanceVmComponent } from './performance/performance-vm/performance-vm.component'; +import { CcvpnNetworkComponent } from './ccvpn-network/ccvpn-network.component'; +import { CcvpnDetailComponent } from './ccvpn-detail/ccvpn-detail.component'; +import { CcvpnCreationComponent } from './ccvpn-creation/ccvpn-creation.component'; + +import { DetailsComponent } from './components/details/details.component'; +import { GraphiclistComponent } from './components/graphiclist/graphiclist.component'; + +import { BarComponent } from './components/charts/bar/bar.component'; +import { LineComponent } from './components/charts/line/line.component'; +import { PieComponent } from './components/charts/pie/pie.component'; + +import {PathLocationStrategy, LocationStrategy, HashLocationStrategy} from '@angular/common'; +// 自定义服务 +import { MyhttpService } from './myhttp.service'; +import { networkHttpservice } from './networkHttpservice.service'; + +@NgModule({ + providers : [ + { provide: LocationStrategy, useClass: HashLocationStrategy }, + { provide: NZ_I18N, useValue: en_US }, + MyhttpService, + networkHttpservice + ], + declarations: [ + AppComponent, + HomeComponent, + + ServicesComponent, + ServicesListComponent, + OnboardVnfVmComponent, + + AlarmComponent, + + PerformanceComponent, + PerformanceVnfComponent, + PerformanceVmComponent, + DetailsComponent, + PieComponent, + LineComponent, + BarComponent, + GraphiclistComponent, + + CcvpnNetworkComponent, + CcvpnDetailComponent, + CcvpnCreationComponent, + ], + imports: [ + BrowserModule, + FormsModule, + HttpClientModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }), + BrowserAnimationsModule, + NgZorroAntdModule.forRoot(), + NgxEchartsModule, + AppRoutingModule + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { }
\ No newline at end of file diff --git a/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.css b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.css new file mode 100644 index 00000000..35fdbef5 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.css @@ -0,0 +1,195 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 10px; +} +.model { + background-color: #fff; + height: 90%; + overflow-y: auto; +} +.model .back { + position: absolute; + top: 10px; + right: 20px; +} +.model .creation { + position: relative; + width: 60%; + height: 100%; + overflow-y: auto; + border-radius: 5px; + padding: 15px; +} +.model .creation h3.title { + height: 20px; + font: 700 20px/20px "Arial"; + color: #666; +} +.model .creation h3 { + height: 20px; + font: 700 16px/20px "Arial"; + margin: 5px 0px; + color: #000; +} +.model .creation .submit { + position: absolute; + top: 10px; + right: 20px; +} +/* SOTN VPN */ +.model .creation .sotnvpn ul li { + display: inline-block; + height: 35px; + width: 49.5%; +} +.model .creation .sotnvpn ul li span { + display: inline-block; + width: 110px; + font: 700 14px "Arial"; + color: #3fa8eb; + vertical-align: middle; +} +.model .creation .sotnvpn ul li input { + width: 165px; +} +/* Site List */ +/* addsite model */ +.model .sitemodel { + position: absolute; + z-index: 10; + left: 10px; + top: 60px; + background-color: #fff; + box-shadow: 0px 0px 20px #000; + width: 60%; + max-height: 90%; + border-radius: 5px; + overflow-y: auto; +} +.model .sitemodel h3 { + height: 30px; + font: 700 16px/30px "Arial"; + border-bottom: 1px solid #aaa; + padding-left: 10px; +} +.model .sitemodel h4 { + height: 30px; + font: 700 16px/30px "Arial"; + padding-left: 10px; + background-color: #ddd; +} +.model .sitemodel .inputs { + padding: 10px 20px 0; +} +.model .sitemodel .inputs ul li { + display: inline-block; + height: 35px; + width: 49.5%; +} +.model .sitemodel .inputs ul li span { + display: inline-block; + width: 110px; + font: 700 14px "Arial"; + color: #3fa8eb; + vertical-align: middle; +} +.model .sitemodel .inputs input { + width: 165px; +} +.model .sitemodel .action { + float: left; + padding: 10px; +} + +.model nz-table tbody td i.anticon:hover { + color: #3fa8eb; + cursor: pointer; +} + +/* site table */ +.model .site nz-table tbody td i.anticon:hover { + color: #3fa8eb; + cursor: pointer; +} +/* WAN Port */ + +/* Site Group List */ +.model .sitegroup .sitegroupmodal { + position: absolute; + z-index: 10; + left: 200px; + top: 300px; + background-color: #fff; + box-shadow: 0px 0px 20px #000; + width: 330px; + border-radius: 5px; +} +.model .sitegroup .sitegroupmodal h3 { + height: 30px; + font: 700 16px/30px "Arial"; + border-bottom: 1px solid #aaa; + padding-left: 10px; +} +.model .sitegroup .sitegroupmodal .inputs { + padding: 10px 20px 0; +} +.model .sitegroup .sitegroupmodal span { + display: inline-block; + width: 100px; + margin-bottom: 10px; +} +.model .sitegroup .sitegroupmodal .inputs input { + width: 165px; +} +.model .sitegroup .sitegroupmodal .action { + float: right; + padding: 10px; +} + + +/* 图 */ +.model .chart { + width: 40%; + padding: 10px; + height: 100%; + border-left: 10px solid #f3f3f3; +} +.model .chart #createChart { + width: 100%; + height: 80%; + margin-top: 20px; + position: relative; +} +.model .chart #createChart .siteNameP { + position: fixed; + border: 5px; + padding: 3px 5px; + color: #fff; + background: #999; + box-shadow: 0px 0px 20px #000; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.html b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.html new file mode 100644 index 00000000..15486238 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.html @@ -0,0 +1,313 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> Services List </h3> +<hr> +<div class="model"> + <!-- 创建数据 --> + <button class="back" nz-button (click)="goback()"><span><i class="anticon anticon-rollback"></i></span></button> + <div class="creation fl"> + <h3 class="title">{{createParams.commonParams.templateType}} Instance Creation</h3> + <div class="sotnvpn clearfix"> + <h3>SOTN VPN Info</h3> + <ul> + <li><span>Name:</span> <input nz-input [(ngModel)]="sotnInfo.name"></li> + <li><span>Description:</span> <input nz-input [(ngModel)]="sotnInfo.description"></li> + <li><span>Start Time:</span> + <nz-date-picker [(ngModel)]="sotnInfo.startTime" + (ngModelChange)="startTimeChange($event)" + nzPlaceHolder="start time" + nzShowTime> + </nz-date-picker> + </li> + <li><span>End Time:</span> + <nz-date-picker [(ngModel)]="sotnInfo.endTime" + (ngModelChange)="endTimeChange($event)" + nzPlaceHolder="end time" + nzShowTime> + </nz-date-picker> + </li> + <li><span>COS:</span> + <nz-select style="width: 165px;" [(ngModel)]="sotnInfo.COS" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="premium" nzLabel="premium"></nz-option> + <nz-option nzValue="standard" nzLabel="standard"></nz-option> + </nz-select> + </li> + <li><span>Reroute Enabled:</span> <nz-switch [(ngModel)]="sotnInfo.reroute"></nz-switch> </li> + <li><span>Service Level Specification:</span> <input nz-input [(ngModel)]="sotnInfo.SLS"></li> + <li><span>Dual Link:</span> + <nz-select style="width: 165px;" [(ngModel)]="sotnInfo.dualLink" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="no_protection" nzLabel="no_protection"></nz-option> + <nz-option nzValue="static_1+1" nzLabel="static_1+1"></nz-option> + <nz-option nzValue="permanent_1+1" nzLabel="permanent_1+1"></nz-option> + </nz-select> + </li> + <li><span>CIR:</span> <input nz-input [(ngModel)]="sotnInfo.CIR"></li> + <li><span>EIR:</span> <input nz-input [(ngModel)]="sotnInfo.EIR"></li> + <li><span>CBS:</span> <input nz-input [(ngModel)]="sotnInfo.CBS"></li> + <li><span>EBS:</span> <input nz-input [(ngModel)]="sotnInfo.EBS"></li> + <li><span>Color Aware:</span> <nz-switch [(ngModel)]="sotnInfo.colorAware"></nz-switch> </li> + <li><span>Coupling Flag:</span> <nz-switch [(ngModel)]="sotnInfo.couplingFlag"></nz-switch> </li> + </ul> + </div> + + <div class="site"> + <h3>Site List</h3> + <button nz-button (click)="addSite()">Add Site</button> + <nz-table #siteTable [nzData]="siteTableData" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="15%"> Name </th> + <th nzWidth="15%"> Description </th> + <th nzWidth="15%"> Post Code </th> + <th nzWidth="15%"> Address </th> + <th nzWidth="15%"> VLAN </th> + <th nzWidth="15%"> Action </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="siteTable.data" let-i="index"> --> + <tr *ngFor="let item of siteTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.baseData.name}}</td> + <td>{{item.baseData.description}}</td> + <td>{{item.baseData.postcode}}</td> + <td>{{item.baseData.address}}</td> + <td>{{item.baseData.vlan}}</td> + <td> + <span class="action" (click)="editSite(i+1)"><i class="anticon anticon-edit"></i></span> + <span class="action" (click)="deleteSite(i+1)"><i class="anticon anticon-delete"></i></span> + </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> + + <div class="sitegroup" *ngIf="createParams.commonParams.templateType == 'CCVPN'"> + <h3>Site_Group List</h3> + <button nz-button (click)="addSiteGroup()">Add Group</button> + <div class="sitegroupmodal" *ngIf="siteGroupModelShow"> + <h3>Site_Group</h3> + <div class="inputs"> + <span>Group Name:</span> <input nz-input [(ngModel)]="siteGroupModelData.name"> <br> + <span>Topology:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteGroupModelData.topology" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="full-mesh" nzLabel="full-mesh"></nz-option> + <nz-option nzValue="hub-spoke" nzLabel="hub-spoke"></nz-option> + </nz-select> + <nz-table #groupModalTable [nzData]="siteGroupModalTableData" [nzLoading]="loading" [nzShowPagination]="false" nzSize="small"> + <thead> + <tr> + <th nzShowCheckbox [(nzChecked)]="allChecked" [nzIndeterminate]="indeterminate" (nzCheckedChange)="groupModal_checkAll($event)"></th> + <th> Site Name</th> + <th> Role</th> + </tr> + </thead> + <tbody> + <tr *ngFor="let item of groupModalTable.data; let i = index;"> + <td nzShowCheckbox [nzDisabled]="item.disabled" [(nzChecked)]="item.checked" (nzCheckedChange)="refreshStatus($event)"></td> + <td>{{ item.siteName }}</td> + <td> + <nz-select style="width: 80px;" [(ngModel)]="item.role" + nzAllowClear nzPlaceHolder="Choose" + [nzDisabled]="siteGroupModelData.topology != 'hub-spoke'"> + <nz-option nzValue="hub" nzLabel="hub"></nz-option> + <nz-option nzValue="spoke" nzLabel="spoke"></nz-option> + </nz-select> + </td> + </tr> + </tbody> + </nz-table> + </div> + <div class="action"> + <button nz-button nzType="primary" (click)="addsitegroup_OK()">OK</button> + <button nz-button nzType="primary" (click)="addsitegroup_cancel()">Cancel</button> + </div> + </div> + <nz-table #siteGroupTable [nzData]="siteGroupTableData" + [nzLoading]="loading" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="20%"> Group Name </th> + <th nzWidth="20%"> Topology </th> + <th nzWidth="20%"> Sites </th> + <th nzWidth="15%"> Role </th> + <th nzWidth="15%"> Action </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="siteGroupTable.data" let-i="index"> --> + <tr *ngFor="let item of siteGroupTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.topology}}</td> + <td>{{item.sites}}</td> + <td>{{item.role}}</td> + <td> + <span class="action" (click)="editGroupSite(i+1)"><i class="anticon anticon-edit"></i></span> + <span class="action" (click)="deleteGroupSite(i+1)"><i class="anticon anticon-delete"></i></span> + </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> + + <button class="submit" nz-button [nzType]="'primary'" (click)="submit()"><span>Create</span></button> + </div> + <!-- site模态框 --> + <div class="sitemodel" *ngIf="siteModelShow"> + <h3>Site_Enterprise Service</h3> + <div class="inputs"> + <ul> + <li><span>Name:</span> <input nz-input [(ngModel)]="siteBaseData.name"></li> + <li><span>Description:</span> <input nz-input [(ngModel)]="siteBaseData.description"></li> + <li *ngIf="createParams.commonParams.templateType == 'CCVPN'"><span>Type:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteBaseData.type" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="single-gateway" nzLabel="single-gateway"></nz-option> + <nz-option nzValue="dual-gateway" nzLabel="dual-gateway"></nz-option> + </nz-select> + </li> + <li *ngIf="createParams.commonParams.templateType == 'CCVPN'"><span>Role:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteBaseData.role" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="dsvpn-hub" nzLabel="dsvpn-hub"></nz-option> + <nz-option nzValue="sd-wan-edge" nzLabel="sd-wan-edge"></nz-option> + </nz-select> + </li> + <li><span>PostCode:</span> <input nz-input [(ngModel)]="siteBaseData.postcode"></li> + <li><span>VLAN:</span> <input nz-input [(ngModel)]="siteBaseData.vlan"></li> + <li><span>Address:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteBaseData.address" nzAllowClear nzPlaceHolder="Choose"> + <nz-option *ngFor="let item of siteModeAddress" nzValue="{{item}}" nzLabel="{{item}}"></nz-option> + </nz-select> + </li> + </ul> + </div> + <div *ngIf="createParams.commonParams.templateType == 'CCVPN'"> + <h4>CPE</h4> + <div class="inputs"> + <ul> + <li><span>Name:</span> <input nz-input [(ngModel)]="siteCpeData.device_name"></li> + <li><span>Version:</span> <input nz-input [(ngModel)]="siteCpeData.device_version"></li> + <li><span>ESN:</span> <input nz-input [(ngModel)]="siteCpeData.device_esn"></li> + <li><span>Class:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteCpeData.device_class" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="VNF" nzLabel="VNF"></nz-option> + <nz-option nzValue="PNF" nzLabel="PNF"></nz-option> + </nz-select> + </li> + <li><span>System IP:</span> <input nz-input [(ngModel)]="siteCpeData.device_systemIp"></li> + <li><span>Vendor:</span> <input nz-input [(ngModel)]="siteCpeData.device_vendor"></li> + <li><span>Type:</span> <input nz-input [(ngModel)]="siteCpeData.device_type"></li> + </ul> + </div> + <h4>WAN Port</h4> + <div> + <nz-table #siteModalTable [nzData]="siteWanData" + [nzLoading]="loading" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="15%"> Name </th> + <th nzWidth="15%"> Description </th> + <th nzWidth="15%"> PortType </th> + <th nzWidth="18%"> PortNumber </th> + <th nzWidth="17%"> IPAddress </th> + <th nzWidth="10%"> Action </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="siteModalTable.data" let-i="index"> --> + <tr *ngFor="let item of siteModalTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.sitewanport_name}}</td> + <td>{{item.sitewanport_description}}</td> + <td>{{item.sitewanport_portType}}</td> + <td>{{item.sitewanport_portNumber}}</td> + <td>{{item.sitewanport_ipAddress}}</td> + <td> + <span class="action" (click)="editWanPort(i+1)"><i class="anticon anticon-edit"></i></span> + </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> + <nz-modal [(nzVisible)]="wanPortModal" nzWidth="400" nzTitle="WAN Port Edit" (nzOnCancel)="wanPortModal_Cancel()" (nzOnOk)="wanPortModal_Ok()"> + <ul class="wanPortModalList"> + <li><span>Name:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_name"></li> + <!-- <li><span>Device Name:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_deviceName"></li> --> + <li><span>Description:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_description"></li> + <li><span>Port Type:</span> + <nz-select style="width: 165px;" [(ngModel)]="siteWanParams.sitewanport_portType" nzAllowClear nzPlaceHolder="Choose"> + <nz-option nzValue="GE" nzLabel="GE"></nz-option> + <nz-option nzValue="FE" nzLabel="FE"></nz-option> + <nz-option nzValue="XGE" nzLabel="XGE"></nz-option> + <nz-option nzValue="LTE" nzLabel="LTE"></nz-option> + <nz-option nzValue="xDSL(ATM)" nzLabel="xDSL(ATM)"></nz-option> + <nz-option nzValue="xSDL(PTM)" nzLabel="xSDL(PTM)"></nz-option> + </nz-select> + </li> + <li><span>Port Number:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_portNumber"></li> + <li><span>Ip Address:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_ipAddress"></li> + <li><span>Provider IP Address:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_providerIpAddress"></li> + <li><span>Transport Nerwork:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_transportNetworkName"></li> + <li><span>Input Bandwidth:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_inputBandwidth"></li> + <li><span>Output Bandwidth:</span> <input nz-input [(ngModel)]="siteWanParams.sitewanport_outputBandwidth"></li> + </ul> + </nz-modal> + </div> + + <div class="action"> + <button nz-button nzType="primary" (click)="addsite_OK()">OK</button> + <button nz-button nzType="primary" (click)="addsite_cancel()">Cancel</button> + </div> + </div> + <!-- 图 --> + <div class="chart fr"> + Create Service + {{createParams.commonParams.templateType}} + <div id="createChart"> + <svg width="100%" height="100%"> + <line *ngFor="let item of lines" x1=50% y1="45%" [attr.x2]="item.x2" y2="72%" style="stroke:#3fa8eb;stroke-width:2"/> + <image xlink:href="./assets/images/cloud-site.png" + x="25%" y="30%" width="50%"/> + <!-- <text dx="42%" dy="45%" style="font:700 18px 'Arial';fill:#666">{{createParams.commonParams.templateType}}</text> --> + <text dx="42%" dy="45%" style="font:700 18px 'Arial';fill:#666"></text> + <g *ngFor="let item of siteImage" + (mouseover)="showSite($event,item)" + (mousemove)="moveSite($event,item)" + (mouseout)="hideSite($event)"> + <image + xlink:href="./assets/images/site.png" + [attr.x]="item.x" y="65%" width="80px"/> + <text [attr.dx]="item.x + 25" dy="72%" style="font:700 16px 'Arial';fill:#666">{{ item.name }}</text> + </g> + + </svg> + <!-- <p class="siteNameP" [ngStyle]="siteNameStyle">{{ siteName }}</p> --> + </div> + </div> + +</div> diff --git a/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.spec.ts b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.spec.ts new file mode 100644 index 00000000..922bd9ea --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CcvpnCreationComponent } from './ccvpn-creation.component'; + +describe('CcvpnCreationComponent', () => { + let component: CcvpnCreationComponent; + let fixture: ComponentFixture<CcvpnCreationComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CcvpnCreationComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CcvpnCreationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.ts b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.ts new file mode 100644 index 00000000..ffc354c7 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-creation/ccvpn-creation.component.ts @@ -0,0 +1,548 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { MyhttpService } from '../myhttp.service'; + +@Component({ + selector: 'app-ccvpn-creation', + templateUrl: './ccvpn-creation.component.html', + styleUrls: ['./ccvpn-creation.component.css'] +}) +export class CcvpnCreationComponent implements OnInit { + + constructor(private myhttp:MyhttpService) { } + + ngOnInit() { + this.getSiteAddressList(); + this.getTemParameters(); + } + + @Input() createParams; + @Input() namesTranslate; //输入项参数名字转换 + @Output() closeCreate = new EventEmitter(); + + templateParameters = {}; + getTemParameters(){ //获取模板参数 + let chosedtemplates = Object.values(this.createParams.templates); + // console.log(this.createParams); + console.log(chosedtemplates); //模板id数组 + let types = ["sotnvpn","site","sdwanvpn"]; + chosedtemplates.forEach((item,index)=>{ + this.myhttp.getTemplateParameters(types[index],item) + .subscribe((data)=>{ + if(index === 0){ + this.templateParameters["sotnvpn"] = data; + this.sotnNames = data.inputs.map((item)=>{return item.name}); //云的真实名字 + }else if(index === 1){ + this.templateParameters["site"] = data; + let wanportnames = {}; + this.siteNames = data.inputs.map((item)=>{return item.name}); //site中所有真实名字,没有分组,放在一起了 + this.siteNames.forEach((item)=>{ + if(item.includes("_device_")){ + this.siteCpeNames.push(item); + }else if(item.includes("_sitewanport_")){ + let firstName = item.split("_")[0]; + wanportnames[firstName]?wanportnames[firstName].push(item):wanportnames[firstName]=[item]; + }else { + this.siteBaseNames.push(item); + } + }) + this.siteWanNames = Object.values(wanportnames); + this.siteWanNames.forEach((item)=>{ + this.siteWanData.push(this.siteWanParams); //根据wanport组添加表格中 + }) + // console.log(this.sotnNames) + // console.log(this.siteNames) + // console.log(this.siteBaseNames) + // console.log(this.siteCpeNames) + // console.log(this.siteWanNames) + // console.log(this.siteWanData) + }else if(index === 2){ + this.templateParameters["sdwan"] = data; + this.siteGroupNames = data.inputs.map((item)=>{return item.name}); //sdwanvpn真实名字 + // console.log(this.siteGroupNames); + } + + },(err)=>{ + + }) + }) + + } + // SOTN VPN Info 输入参数 + sotnInfo = { + name:null,description:null, + startTime:null,endTime:null, + COS:"premium",reroute:false, + SLS:null,dualLink:false, + CIR:null,EIR:null, + CBS:null,EBS:null, + colorAware:false,couplingFlag:false + } + sotnNames = [] //真实名字 + + startTimeChange(event){ + console.log(event) + } + endTimeChange(event){ + console.log(event) + } + + + // Site List + siteTableData = [ + + ] + siteModeAddress = [];//site地址,筛选框数据,本地配置文件 + siteNames = [];//site中所有真实名字,未分组,模拟真实请求情况; + + siteBaseData = { //模态框数据,输入参数,绑定数据 + name:null, + description:null, + type:null, + role:null, + postcode:null, + address:null, + vlan:null, + sotnVpnName:null, //SOTN VPN Info中name + controlPoint:null, //site group里面site的Role设置为spoke时,传递site group里面Role设置为hub的site name;否则传递空白 + groupRole:null, //site group的role + groupName:null, //site group的name + emails:null,// 不显示传空 + latitude:null,// + longitude:null,// + clientSignal:null// + }; + siteBaseNames = [] //真实名字 + // cpe 编辑 + siteCpeData = { + device_name:null, + device_version:null, + device_esn:null, + device_class:null, + device_systemIp:null, + device_vendor:null, + device_type:null + }; + siteCpeNames = [] //真实名字 + // Wan Port 编辑 + siteWanData = []; //wan port 表格绑定数据 + siteWanParams = { //每一行数据详细参数,模态框 + sitewanport_name:null, + sitewanport_deviceName:null, + sitewanport_description:null, + sitewanport_portType:null, + sitewanport_portNumber:null, + sitewanport_ipAddress:null, + sitewanport_providerIpAddress:null, + sitewanport_transportNetworkName:null, + sitewanport_inputBandwidth:null, + sitewanport_outputBandwidth:null + }; + siteWanNames = [] //真实名字 + wanPortModal = false; //模态框显示隐藏 + wanPortEditNum = 0;//编辑哪行 + editWanPort(num){ + this.wanPortModal = true; + this.wanPortEditNum = num; + this.siteWanParams = Object.assign({},this.siteWanData[num-1]); + } + wanPortModal_Ok(){ + let inputsData = Object.assign({},this.siteWanParams); //新建对象,断开原引用,因为后面要清空模态框 + inputsData.sitewanport_deviceName = this.siteCpeData.device_name; + this.siteWanData[this.wanPortEditNum-1] = inputsData; + this.siteWanData = [...this.siteWanData]; //表格刷新 + Object.keys(this.siteWanParams).forEach((item)=>{ //清空模态框 + this.siteWanParams[item] = null; + }) + this.wanPortModal = false; + console.log(this.siteWanData) + } + wanPortModal_Cancel(){ + this.wanPortModal = false; + } + + + // 获取site地址,手动文件 + getSiteAddressList(){ + this.myhttp.getSiteAddress() + .subscribe((data)=>{ + console.log(data); + this.siteModeAddress = data.map((item)=>{ return item.location}); + },(err)=>{ + console.log(err); + }) + } + siteModelShow = false; + addSite(){ + this.siteModelShow = true; + this.isEdit = 0; + } + // addsite模态框按钮 + isEdit = 0; //编辑序号,No值,0表示增加 + addsite_OK(){ + this.siteBaseData.sotnVpnName = this.sotnInfo.name; + // let inputsData = Object.assign({},this.siteBaseData,this.siteCpeData,this.siteWanData); //新建对象,断开原引用,因为后面要清空模态框 + let inputs = {}; + inputs["baseData"] = Object.assign({},this.siteBaseData); + inputs["cpeData"] = Object.assign({},this.siteCpeData); + inputs["wanportData"] = this.siteWanData.map((item)=>{ + return Object.assign({},item); + }) + console.log(inputs); + if(this.isEdit){ + // 编辑状态不增加 + this.siteTableData[this.isEdit-1] = inputs; + this.siteTableData = [...this.siteTableData]; //表格刷新 + this.siteGroupTableData.forEach((item)=>{ //site修改名字后,更新组中sites值 + if(item.sites.split(";").filter((d)=>{return d!=""}).includes(this.lastSiteName)){ + item.sites = item.sites.replace(this.lastSiteName,this.siteBaseData.name); + } + }) + }else{ + // this.siteTableData.push(inputs);//使用 push 或者 splice 修改 nzData 失效 当加上[nzFrontPagination]="false" 时,生效 + this.siteTableData = [...this.siteTableData,inputs]; + } + + Object.keys(this.siteBaseData).forEach((item)=>{ //清空模态框 + this.siteBaseData[item] = null; + }) + Object.keys(this.siteCpeData).forEach((item)=>{ //清空模态框 + this.siteCpeData[item] = null; + }) + this.siteWanData.forEach((item)=>{ + Object.keys(item).forEach((item2)=>{ + item[item2] = null; + }) + }) + // console.log(this.siteTableData); + this.lastSiteName = null; + this.drawImage(this.siteTableData); + this.siteModelShow = false; + } + addsite_cancel(){ + Object.keys(this.siteBaseData).forEach((item)=>{ //清空模态框 + this.siteBaseData[item] = null; + }) + Object.keys(this.siteCpeData).forEach((item)=>{ //清空模态框 + this.siteCpeData[item] = null; + }) + this.siteWanData.forEach((item)=>{ + Object.keys(item).forEach((item2)=>{ + item[item2] = null; + }) + }) + this.lastSiteName = null; + this.siteModelShow = false; + } + lastSiteName = null; //当site修改之后,若修改了名字,则需要更新group中sites的名字 + editSite(num){ //编辑修改选中site信息 + this.siteModelShow = true; + this.isEdit=num; + this.siteBaseData = Object.assign({},this.siteTableData[num-1].baseData); + this.siteCpeData = Object.assign({},this.siteTableData[num-1].cpeData); + this.siteWanData = this.siteTableData[num-1].wanportData.map((item)=>{return Object.assign({},item)}); + this.lastSiteName = this.siteBaseData.name; + } + deleteSite(num){ + let deleteSiteName = this.siteTableData[num-1].baseData.name; //删除的site中name + let groupSites = []; + this.siteGroupTableData.forEach((item)=>{ groupSites.push(...item.sites.split(";").filter((d)=>{return d!=""})) }); + if(groupSites.includes(deleteSiteName)){ + alert("this site has in grouplist;can't delete!") + return false; + } + this.siteTableData = this.siteTableData.filter((d,i) => i !== num-1); + // this.siteTableData.splice(num-1,1); //模板中加上[nzFrontPagination]="false" 时,生效 + this.drawImage(this.siteTableData); + + // let groupIndex = this.siteGroupTableData.findIndex((item)=>{return item.sites.split(";").includes(deleteSiteName)}); + // console.log(groupIndex) + // this.deleteGroupSite(groupIndex + 1); //删除时首行编号为1 + } + + // site节点图形描绘 + lines=[]; + siteImage=[]; + drawImage(sitelist){ + let cx = 200; + let cy = 200; + let r = 180; + let startAngle = -210 * (Math.PI/180); + let step = sitelist.length > 1 ? 120/(sitelist.length-1) * (Math.PI/180) : 1; + + this.lines = sitelist.map((item,index)=>{ + let x = cx + Math.cos(startAngle - step*index)*r; + let y = cy + Math.sin(startAngle - step*index)*r; + return {img:"line",site:item.baseData.name,x1:cx,y1:cy,x2:x,y2:y} + }) + this.siteImage = this.lines.map((item)=>{ + return {img:"site",name:item.site,x:item.x2 - 40,y:item.y2 - 40} + }) + } + + siteName=null; + siteNameStyle = { + 'display':'none', + 'left':'0', + 'top':'0' + } + showSite($event,item){ + this.siteName = item.name; + this.siteNameStyle.display = 'block'; + } + moveSite($event,item){ + this.siteNameStyle.left = $event.clientX + "px"; + this.siteNameStyle.top = $event.clientY - 35 + "px"; + } + hideSite($event){ + this.siteNameStyle.display = 'none'; + } + // siteGroup List + siteGroupTableData = [ + + ] + siteGroupModelData = { + name:null, + topology:null, + sites:null, + role:null + } + siteGroupModelShow = false; + siteGroupModalTableData = [];// ==> siteTableData? + siteGroupNames=[]; //sdwanvpn真实名字 + + // 勾选框 + allChecked = false; + indeterminate = false; + groupModal_checkAll(value){ + this.siteGroupModalTableData.forEach(data => { + if (!data.disabled) { + data.checked = value; + } + }); + this.refreshStatus(); + } + refreshStatus(){ + const allChecked = this.siteGroupModalTableData.filter(item => !item.disabled).every(item => item.checked === true); + const allUnChecked = this.siteGroupModalTableData.filter(item => !item.disabled).every(item => !item.checked); + this.allChecked = allChecked; + this.indeterminate = (!allChecked) && (!allUnChecked); + } + + addSiteGroup(){ + this.isGroupEdit = 0; + this.siteGroupModelShow = true; + let checkedSite = this.siteGroupTableData.map((item)=>{return item.sites}).join(";").split(";").filter((d)=>{return d!=""});//循环组中是否已经选用了某个site,若存在 则新组不可选 + // console.log(checkedSite); + this.siteTableData.forEach((item,index)=>{ + if(checkedSite.includes(item.baseData.name)){ + this.siteGroupModalTableData.push({siteName:item.baseData.name,role:null,checked:false,disabled:true}) + }else { + this.siteGroupModalTableData.push({siteName:item.baseData.name,role:null,checked:false,disabled:false}) + } + }) + } + // addsiteGroup模态框按钮 + addsitegroup_OK(){ //将模态框中的值赋给表中对应项--->将选中的site中的groupRole、groupName、controlPoint更新---> + //拷贝数据判断是增加或编辑,更新表中数据---> 清除模态框中数据,便于下次添加,关闭模态框 + console.log(this.siteGroupModalTableData); + this.siteGroupModelData.sites=""; //置空组成员名字,写成""方便+= ,若为null +=时会转成 "null" + this.siteGroupModelData.role=""; // + let site_controlPoint = this.siteGroupModalTableData.map((item)=>{ if(item.checked&&item.role=="hub"){ return item.siteName}}).filter((item)=>{return item!=undefined}); + // console.log(site_controlPoint); + this.siteGroupModalTableData.forEach((item,index)=>{ //模态框中site顺序和 表中site顺序一致 + if(item.checked){ + this.siteGroupModelData.sites += item.siteName+";"; + this.siteGroupModelData.role += item.role+";"; + this.siteTableData[index].baseData.groupRole = item.role; //site group的role + this.siteTableData[index].baseData.groupName = this.siteGroupModelData.name; //site group的name + if(item.role == "spoke"){ + this.siteTableData[index].baseData.controlPoint = site_controlPoint.join(); //site group里面site的Role设置为spoke时,传递site group里面Role设置为hub的site name;否则传递空白 + } + } + }) + + let inputsData = {}; + Object.assign(inputsData,this.siteGroupModelData); + if(this.isGroupEdit){ + // 编辑状态不增加 + this.siteGroupTableData[this.isGroupEdit-1] = inputsData; + this.siteGroupTableData = [...this.siteGroupTableData]; //表格刷新 + }else{ + // this.siteTableData.push(inputsData);//使用 push 或者 splice 修改 nzData 失效 + this.siteGroupTableData = [...this.siteGroupTableData,inputsData]; + } + + Object.keys(this.siteGroupModelData).forEach((item)=>{ + this.siteGroupModelData[item] = null; + }) + this.siteGroupModalTableData = []; + this.siteGroupModelShow = false; + } + addsitegroup_cancel(){ + this.siteGroupModalTableData = []; + this.siteGroupModelShow = false; + } + isGroupEdit = 0; //编辑序号,No值,0表示增加 + editGroupSite(num){ //将当前编辑的行数据填入模态框--->获取当前编辑项sites名--->判断更新模态框中site项状态 + this.siteGroupModelShow = true; + this.isGroupEdit=num; + this.siteGroupModelData = Object.assign({},this.siteGroupTableData[num-1]); + console.log(this.siteGroupModelData) + let editSites = this.siteGroupTableData[num-1].sites.split(";").filter((item)=>{return item!=""}); //获取组中的site名 + // console.log(editSites); + let checkedSite = this.siteGroupTableData.map((item)=>{return item.sites}).join(";").split(";").filter((d)=>{return d!=""});//循环组中是否已经选用了某个site,若存在 则新组不可选 + // console.log(checkedSite); + this.siteTableData.forEach((item,index)=>{ + if(editSites.includes(item.baseData.name)){//先将编辑组中的site 中这三个值还原,否则减少某个site时 不会更新不选中的 + item.baseData.groupRole = null; //site group的role + item.baseData.groupName = null; //site group的name + item.baseData.controlPoint = null; + this.siteGroupModalTableData.push({siteName:item.baseData.name,role:item.baseData.groupRole,checked:true,disabled:false}) + }else + if(checkedSite.includes(item.baseData.name)){ + this.siteGroupModalTableData.push({siteName:item.baseData.name,role:null,checked:false,disabled:true}) + }else { + this.siteGroupModalTableData.push({siteName:item.baseData.name,role:null,checked:false,disabled:false}) + } + }) + + } + deleteGroupSite(num){ + let deleteSiteGroupsites = this.siteGroupTableData[num-1].sites.split(";").filter((item)=>{return item!=""}); //删除的site中name + this.siteGroupTableData = this.siteGroupTableData.filter((d,i) => i !== num-1); + this.siteTableData.forEach((item,index)=>{ + if(deleteSiteGroupsites.includes(item.baseData.name)){ + item.baseData.groupRole = null; //site group的role + item.baseData.groupName = null; //site group的name + item.baseData.controlPoint = null; + } + }) + } + + + + // 提交创建数据 + submit(){ + let globalCustomerId = this.createParams.commonParams.customer.id; + let globalServiceType = this.createParams.commonParams.serviceType; + let sotnInputs = {}; + // 由于请求模板不一样,所以外层需要循环请求回来的真实名字,内层循环本地参数,将当前值赋给真实名字 + this.sotnNames.forEach((name)=>{ + for(let key in this.sotnInfo){ + let nameParts = this.namesTranslate.sotnNameTranslate[key].split("_"); + if(name.startsWith(nameParts[0])&&name.endsWith(nameParts[1])){ + sotnInputs[name] = this.sotnInfo[key]; + break; + } + } + }) + console.log(sotnInputs); + let vpnbody = { + service:{ + name:this.sotnInfo.name, + description:this.sotnInfo.description, + serviceInvariantUuid:this.templateParameters["sotnvpn"].invariantUUID, //template.invariantUUID, //serviceDefId + serviceUuid:this.templateParameters["sotnvpn"].uuid, //template.uuid, // uuid ?? templateId + globalSubscriberId:globalCustomerId, //customer.id + serviceType:globalServiceType, //serviceType.value + parameters:{ + locationConstraints:[], + resources:[], + requestInputs:sotnInputs + } + } + } + + let sitebody = this.siteTableData.map((site)=>{ + let siteInputs = {}; + this.siteBaseNames.forEach((basename)=>{ + for(let key in site.baseData){ + let namePart = this.namesTranslate.siteNameTranslate.baseNames[key]; + if(basename.endsWith(namePart)){ + siteInputs[basename] = site.baseData[key]; + break; + } + } + }) + this.siteCpeNames.forEach((cpename)=>{ + for(let key in site.cpeData){ + let namePart = this.namesTranslate.siteNameTranslate.cpeNames[key]; + if(cpename.endsWith(namePart)){ + siteInputs[cpename] = site.cpeData[key]; + break; + } + } + }) + this.siteWanNames.forEach((item,index)=>{ + item.forEach((wanportname)=>{ + for(let key in site.wanportData[index]){ + let namePart = this.namesTranslate.siteNameTranslate.wanportNames[key]; + if(wanportname.endsWith(namePart)){ + siteInputs[wanportname] = site.wanportData[index][key]; + break; + } + } + }) + }) + + return { + service:{ + name:site.baseData.name, + description:site.baseData.description, + serviceInvariantUuid:this.templateParameters["site"].invariantUUID, + serviceUuid:this.templateParameters["site"].uuid, + globalSubscriberId:globalCustomerId, + serviceType:globalServiceType, + parameters:{ + locationConstraints:[], + resources:[], + requestInputs:siteInputs + } + } + } + }); + console.log(sitebody); + + let groupbody = this.siteGroupTableData.map((item)=>{ + let siteGroupInputs = {}; + this.siteGroupNames.forEach((name)=>{ + for(let key in item){ + let nameParts = this.namesTranslate.siteGroupNameTranslate[key].split("_"); + if(name.startsWith(nameParts[0])&&name.endsWith(nameParts[1])){ + siteGroupInputs[name] = item[key]; + break; + } + } + }) + return { + service:{ + name:item.name, + description:item.topology, + serviceInvariantUuid:this.templateParameters["sdwan"].invariantUUID, + serviceUuid:this.templateParameters["sdwan"].uuid, + globalSubscriberId:globalCustomerId, + serviceType:globalServiceType, + parameters:{ + locationConstraints:[], + resources:[], + requestInputs:siteGroupInputs + } + } + } + }) + console.log(groupbody); + + let createObj = { + vpnbody:vpnbody, + sitebody:sitebody, + groupbody:groupbody + } + + this.closeCreate.emit(createObj); + + } + + goback(){ + this.closeCreate.emit(); + } +} diff --git a/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.css b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.css new file mode 100644 index 00000000..710bd104 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.css @@ -0,0 +1,138 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 10px; +} +.model { + background-color: #fff; + height: 90%; + overflow-y: auto; +} +.model .back { + position: absolute; + top: 10px; + right: 20px; +} +.model .detaildata { + position: relative; + width: 60%; + height: 100%; + overflow-y: auto; + border-radius: 5px; + padding: 15px; +} +.model .detaildata h3.title { + height: 20px; + font: 700 20px/20px "Arial"; + color: #666; +} +.model .detaildata h3 { + height: 20px; + font: 700 16px/20px "Arial"; + margin: 5px 0px; + color: #000; +} +/* SOTN VPN */ +.model .detaildata .sotnvpn ul li { + display: inline-block; + height: 35px; + width: 49.5%; +} +.model .detaildata .sotnvpn ul li span { + display: inline-block; + width: 110px; + font: 700 14px "Arial"; + color: #3fa8eb; + vertical-align: middle; +} +/* site Detail */ +.model .detaildata .site .siteDetail { + position: fixed; + z-index: 10; + left: 260px; + top: 50px; + background-color: #fff; + box-shadow: 0px 0px 20px #000; + width: 50%; + max-height: 85%; + border-radius: 5px; + overflow-y: auto; +} +.model .detaildata .site h3 { + margin: 0; + height: 30px; + font: 700 16px/30px "Arial"; + padding-left: 10px; + background-color: #ddd; +} +.model .detaildata .site h3 .closeDetail { + cursor: pointer; + padding: 2px 15px; + color: #3fa8eb; +} +.model .detaildata .site ul li { + padding-left: 5px; + display: inline-block; + height: 35px; + width: 32%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.model .detaildata .site ul li span { + display: inline-block; + width: 95px; + font: 700 14px "Arial"; + color: #3fa8eb; + vertical-align: middle; +} + +/* 图 */ +.model .chart { + width: 40%; + padding: 10px; + height: 100%; + border-left: 10px solid #f3f3f3; +} +.model .chart #detailChart { + position: relative; + width: 100%; + height: 80%; + margin-top: 20px; +} +.model .chart #detailChart .cloudcounty { + cursor: pointer; +} + +.model .chart #detailChart .couldDetail { + position: absolute; + left: 50%; + top: 10px; + width: 80%; + transform: translate(-50%,0); + height: 160px; + background-color: #aaa; + border-radius: 5px; + box-shadow: 0px 0px 20px #000; +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.html b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.html new file mode 100644 index 00000000..33d56eb9 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.html @@ -0,0 +1,336 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> Services List </h3> +<hr> +<div class="model"> + <!-- 详情数据 --> + <button class="back" nz-button (click)="goback()"><span><i class="anticon anticon-rollback"></i></span></button> + <div class="detaildata fl"> + <h3 class="title">{{detailParams.sotnvpnSer['service-instance-name']}} Instance Detail</h3> + <div class="sotnvpn clearfix"> + <h3>SOTN VPN Info</h3> + <ul> + <li><span>Name:</span> {{sotnVpnInfo.name}}</li> + <li><span>Description:</span> {{sotnVpnInfo.description}} </li> + <li><span>Start Time:</span> {{sotnVpnInfo.startTime}}</li> + <li><span>End Time:</span> {{sotnVpnInfo.endTime}} </li> + <li><span>COS:</span> {{sotnVpnInfo.COS}}</li> + <li><span>Reroute Enable:</span> {{sotnVpnInfo.reroute}} </li> + <li><span>Service Level Specification:</span> {{sotnVpnInfo.SLS}}</li> + <li><span>DualLink:</span> {{sotnVpnInfo.dualLink}} </li> + <li><span>CIR:</span> {{sotnVpnInfo.CIR}}</li> + <li><span>EIR:</span> {{sotnVpnInfo.EIR}} </li> + <li><span>CBS:</span> {{sotnVpnInfo.CBS}}</li> + <li><span>EBS:</span> {{sotnVpnInfo.EBS}} </li> + <li><span>Color Aware:</span> {{sotnVpnInfo.colorAware}}</li> + <li><span>Coupling Flag:</span> {{sotnVpnInfo.couplingFlag}} </li> + </ul> + </div> + + <div class="site"> + <h3>Site List</h3> + <nz-table #nzTable [nzData]="siteList" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="15%"> Name </th> + <th nzWidth="20%"> Description </th> + <th nzWidth="15%"> Post Code </th> + <th nzWidth="15%"> Address </th> + <th nzWidth="15%"> VLAN </th> + <th nzWidth="10%"> Action </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.baseNames.name}}</td> + <td>{{item.baseNames.description}}</td> + <td>{{item.baseNames.postcode}}</td> + <td>{{item.baseNames.address}}</td> + <td>{{item.baseNames.vlan}}</td> + <td (click)="showSiteDetail(item)"> <a href="javascript:;">Detail</a> </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + <div class="siteDetail" *ngIf="siteDetail"> + <h3>Site_Enterprise Service <span class="closeDetail fr" (click)="siteDetail=false">X</span></h3> + <ul> + <li><span>Name:</span> {{ siteDetailData.baseNames.name}}</li> + <li><span>Description:</span> {{ siteDetailData.baseNames.description }} </li> + <li *ngIf="detailParams.sotnvpnSer.Type == 'CCVPN'"><span>Type:</span> {{ siteDetailData.baseNames.type }} </li> + <li *ngIf="detailParams.sotnvpnSer.Type == 'CCVPN'"><span>Role:</span> {{ siteDetailData.baseNames.role }} </li> + <li><span>PostCode:</span> {{ siteDetailData.baseNames.postcode }} </li> + <li><span>VLAN:</span> {{ siteDetailData.baseNames.vlan }}</li> + <li><span>Address:</span> {{ siteDetailData.baseNames.address }}</li> + <li><span>ClientSignal:</span> {{ siteDetailData.baseNames.clientSignal }}</li> + <li><span>ControlPoint:</span> {{ siteDetailData.baseNames.controlPoint }}</li> + <li><span>Emails:</span> {{ siteDetailData.baseNames.emails }}</li> + <li><span>GroupName:</span> {{ siteDetailData.baseNames.groupName }}</li> + <li><span>GroupRole:</span> {{ siteDetailData.baseNames.groupRole }}</li> + <li><span>Latitude:</span> {{ siteDetailData.baseNames.latitude }}</li> + <li><span>Longitude:</span> {{ siteDetailData.baseNames.longitude }}</li> + <li><span>SotnVpnName:</span> {{ siteDetailData.baseNames.sotnVpnName }}</li> + </ul> + <div *ngIf="detailParams.sotnvpnSer.Type == 'CCVPN'"> + <h3>CPE</h3> + <ul> + <li><span>Name:</span> {{siteDetailData.cpeNames.device_name}}</li> + <li><span>Version:</span> {{ siteDetailData.cpeNames.device_version }} </li> + <li><span>ESN:</span> {{ siteDetailData.cpeNames.device_esn }} </li> + <li><span>Class:</span> {{ siteDetailData.cpeNames.device_class }} </li> + <li><span>System IP:</span> {{ siteDetailData.cpeNames.device_systemIp }} </li> + <li><span>Vendor:</span> {{ siteDetailData.cpeNames.device_vendor }}</li> + <li><span>Type:</span> {{ siteDetailData.cpeNames.device_type }}</li> + </ul> + <h3>WAN Port</h3> + <nz-table #nzTable [nzData]="siteDetailData.wanportNames" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="15%"> Name </th> + <th nzWidth="20%"> Description </th> + <th nzWidth="15%"> PortType </th> + <th nzWidth="15%"> PortNumber </th> + <th nzWidth="15%"> IPAddress </th> + <th nzWidth="10%"> Action </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.sitewanport_name}}</td> + <td>{{item.sitewanport_description}}</td> + <td>{{item.sitewanport_portType}}</td> + <td>{{item.sitewanport_portNumber}}</td> + <td>{{item.sitewanport_ipAddress}}</td> + <td (click)="showWanportDetail(item)"> <a href="javascript:;">Detail</a> </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + <nz-modal [(nzVisible)]="wanPortModal" [nzTitle]="modalTitle" [nzContent]="modalContent" [nzFooter]="modalFooter" (nzOnCancel)="handleCancel()"> + <ng-template #modalTitle> + WAN Port Detail + </ng-template> + + <ng-template #modalContent> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Name </span> {{wanPortDetail.sitewanport_name}}</p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Description: </span> {{wanPortDetail.sitewanport_description}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Port Type: </span> {{wanPortDetail.sitewanport_portType}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Port Number: </span> {{wanPortDetail.sitewanport_portNumber}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Ip Address: </span> {{wanPortDetail.sitewanport_ipAddress}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Provider IP Address: </span> {{wanPortDetail.sitewanport_providerIpAddress}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Transport Nerwork: </span> {{wanPortDetail.sitewanport_transportNetworkName}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Input Bandwidth: </span> {{wanPortDetail.sitewanport_inputBandwidth}} </p> + <p> <span style="color:#3fa8eb;font:700 14px 'Arial';display:inline-block;width:200px;"> Output Bandwidth: </span> {{wanPortDetail.sitewanport_outputBandwidth}} </p> + </ng-template> + + <ng-template #modalFooter> + <!-- <button nz-button nzType="primary" (click)="handleOk()" [nzLoading]="isConfirmLoading">Custom Submit</button> --> + </ng-template> + </nz-modal> + </div> + + </div> + </div> + + <div class="sitegroup" *ngIf="detailParams.sotnvpnSer.Type == 'CCVPN'"> + <h3>Site_Group List</h3> + <nz-table #nzTable [nzData]="siteGroupList" + [nzLoading]="loading" + [nzShowPagination]="false" + nzSize="small"> + <thead> + <tr> + <th nzWidth="10%"> NO. </th> + <th nzWidth="20%"> Group Name </th> + <th nzWidth="20%"> Topology </th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.topology}}</td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> + </div> + <!-- 图 --> + <div class="chart fr"> + + <div id="detailChart"> + <!-- <svg width="100%" height="100%"> + <line *ngFor="let item of lines" [attr.x1]="item.x1" [attr.y1]="item.y1" [attr.x2]="item.x2" [attr.y2]="item.y2" style="stroke:#3fa8eb;stroke-width:2"/> + <image xlink:href="./assets/images/cloud-county.png" + class="cloudcounty" + (click)="toggleClick()" + (mouseover)="hoverShowcould()" + (mouseout)="hoverHidecould()" + x="175" y="175" height="50px" width="50px"/> + <image *ngFor="let item of siteImage" + xlink:href="./assets/images/cloud-city.png" + [attr.x]="item.x" [attr.y]="item.y" height="50px" width="50px"/> + </svg> + <div class="couldDetail" *ngIf="hoverShow || clickShow"> + + </div> --> + <svg width="100%" height="100%" style="position: relative"> + <!--背景大图--> + <image xlink:href="./assets/images/bigcloud.png" + class="backgroundcloud" + width="100%" + style="position: absolute;z-index:-1"> + </image> + <!--tp,site,domain之间的连线--> + <line *ngFor="let item of detailLines" [attr.x1]="item.x1" [attr.y1]="item.y1" [attr.x2]="item.x2" [attr.y2]="item.y2" style="stroke:#FFC000; stroke-width:2"></line> + <line *ngIf="detailSites" x1="45%" y1="30%" x2="75%" y2="20%" style="stroke:#FFC000; stroke-width:2"></line> + <!--本地domain--> + <g class="clouds"> + <image xlink:href="./assets/images/domain.png" + id="domain1" + width="20%" + x="10%" y="10%" + ></image> + <text dx="12%" dy="16%" style="font-size: 14px; fill:#666;width: 20px;"> + {{vpns[0].domain}} + </text> + </g> + <g *ngIf="vpns[1]" class="clouds"> + <image xlink:href="./assets/images/domain.png" + id="domain2" + width="20%" + x="40%" y="13%" + ></image> + <text dx="43%" dy="19%" style="font-size: 14px; fill: #666;width: 20px;"> + {{vpns[1].domain}} + </text> + </g> + <!--domain1下连接的tp--> + <g class="clouds"> + <image xlink:href="./assets/images/tp.png" + class="tp" + id="tp1" + height="16" width="20" + x="15%" y="25%" + ></image> + <text dx="18%" dy="27%" style="font-size: 14px; fill: #666;width: 20px;"> + {{vpns[0].sitetpname}} + </text> + </g> + <g class="clouds"> + <image xlink:href="./assets/images/tp.png" + class="tp" + id="tp2" + height="16" width="20" + x="30%" y="10%" + ></image> + <text dx="30%" dy="6%" style="font-size: 14px; fill: #666;width: 20px;"> + {{vpns[0].othertpname}} + </text> + </g> + <!--domain2下连接的tp--> + <g *ngIf="vpns[1]" class="clouds"> + <image xlink:href="./assets/images/tp.png" + class="tp" + id="tp3" + height="16" width="20" + x="40%" y="10%" + ></image> + <text dx="40%" dy="9%" style="font-size: 14px; fill: #666;width: 20px;"> + {{vpns[1].othertpname}} + </text> + </g> + <g *ngIf="vpns[1]" class="clouds"> + <image xlink:href="./assets/images/tp.png" + class="tp" + id="tp4" + height="16" width="20" + x="43%" y="28%" + ></image> + <text dx="46%" dy="30%" style="font-size: 14px; fill: #666;width: 20px;"> + {{vpns[1].sitetpname}} + </text> + </g> + <!--外部云--> + <g class="clouds"> + <image xlink:href="./assets/images/out-domain.png" + id="extent-cloud" + height="70.8" width="120" + x="66%" y="12%" + ></image> + <text dx="68%" dy="18%" style="font-size: 14px; fill: #666;width: 20px;"> + SP Partent Network + </text> + </g> + <!--本地的2个site--> + <g class="clouds"> + <image xlink:href="./assets/images/site.png" + id="site1" + height="59" width="100" + x="0%" y="48%" + ></image> + <text dx="2%" dy="53%" style="font-size: 14px; fill: #666;width: 20px;"> + {{localSite[0]["service-instance-name"]}} + </text> + </g> + <g *ngIf="!detailSites" class="clouds"> + <image xlink:href="./assets/images/site.png" + id="site2" + height="59" width="100" + x="25%" y="48%" + ></image> + <text dx="26%" dy="54%" style="font-size: 14px; fill: #666;width: 20px;"> + {{localSite[1]["service-instance-name"]}} + </text> + </g> + <!--外部的2个site--> + <g *ngIf="!detailSites" class="clouds"> + <image xlink:href="./assets/images/site.png" + id="site3" + height="59" width="100" + x="50%" y="48%" + ></image> + <text dx="52%" dy="54%" style="font-size: 14px; fill: #666;width: 20px;"> + {{outerSite[1]["service-instance-name"]}} + </text> + </g> + <g class="clouds"> + <image xlink:href="./assets/images/site.png" + id="site4" + height="59" width="100" + x="75%" y="48%" + ></image> + <text dx="80%" dy="54%" style="font-size: 14px; fill: #666;width: 20px;"> + {{outerSite[0]["service-instance-name"]}} + </text> + </g> + </svg> + </div> + </div> + +</div> diff --git a/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.spec.ts b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.spec.ts new file mode 100644 index 00000000..dc5d34d9 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CcvpnDetailComponent } from './ccvpn-detail.component'; + +describe('CcvpnDetailComponent', () => { + let component: CcvpnDetailComponent; + let fixture: ComponentFixture<CcvpnDetailComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CcvpnDetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CcvpnDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.ts b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.ts new file mode 100644 index 00000000..c9d60ef8 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-detail/ccvpn-detail.component.ts @@ -0,0 +1,368 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { MyhttpService } from '../myhttp.service'; +import * as d3 from 'd3'; + +@Component({ + selector: 'app-ccvpn-detail', + templateUrl: './ccvpn-detail.component.html', + styleUrls: ['./ccvpn-detail.component.css'] +}) +export class CcvpnDetailComponent implements OnInit { + + constructor(private myhttp:MyhttpService) { } + + ngOnInit() { + // this.getDetails(); + this.dataInit(); + this.drawImages(); + } + + @Input() detailParams; + @Input() namesTranslate; + @Output() closeDetail = new EventEmitter(); + + sotnVpnInfo:any; + siteList=[]; + siteGroupList=[]; + dataInit(){ + // 循环真实参数,对比名字转换配置文件,将真实参数名字改成通用名字 + this.sotnVpnInfo = JSON.parse(this.detailParams.sotnvpnSer['input-parameters']).service.parameters.requestInputs; + for(let key in this.sotnVpnInfo){ + for(let key2 in this.namesTranslate.sotnNameTranslate){ + let partnames = this.namesTranslate.sotnNameTranslate[key2].split("_"); + if(key.startsWith(partnames[0])&&key.endsWith(partnames[1])){ + this.sotnVpnInfo[key2] = this.sotnVpnInfo[key]; + break; + } + } + } + + this.siteList = this.detailParams.siteSer.map((item)=>{ + return JSON.parse(item['input-parameters']).service.parameters.requestInputs; + }) + this.siteList.forEach((oneSite,idex)=>{ + oneSite["baseNames"]={};oneSite["cpeNames"]={};oneSite["wanportNames"]=[]; + for(let key in oneSite){ + let hasfind = false; + if(key == "baseNames" || key == "cpeNames" || key == "wanportNames"){ continue }; + for(let key2 in this.namesTranslate.siteNameTranslate.baseNames){ + if(key.endsWith(this.namesTranslate.siteNameTranslate.baseNames[key2])){ + oneSite["baseNames"][key2] = oneSite[key]; + hasfind = true; + break; + } + } + if(hasfind){ continue }; + for(let key3 in this.namesTranslate.siteNameTranslate.cpeNames){ + if(key.endsWith(this.namesTranslate.siteNameTranslate.cpeNames[key3])){ + oneSite["cpeNames"][key3] = oneSite[key]; + hasfind = true; + break; + } + } + if(hasfind){ continue }; + let wanportStartName = key.split("_")[0]; + // 先分组,后面再变换名字 + let theItem = oneSite["wanportNames"].find((item,index)=>{ + if(item){ + return Object.keys(item)[0].startsWith(wanportStartName) + } + }) + theItem?theItem[key]=oneSite[key]:oneSite["wanportNames"].push({[key]:oneSite[key]}) + } + let wanportTs = Object.values(this.namesTranslate.siteNameTranslate.wanportNames); + oneSite["wanportNames"].forEach((item)=>{ + for(let key in item){ + let newName = wanportTs.find((name)=>{ + return key.endsWith(name); + }) + newName?item[newName]=item[key]:null; + } + }) + + }) + + this.siteGroupList = this.detailParams.sdwanSer.map((item)=>{ + return JSON.parse(item['input-parameters']).service.parameters.requestInputs; + }) + this.siteGroupList.forEach((oneSiteGroup)=>{ + for(let key in oneSiteGroup){ + for(let key2 in this.namesTranslate.siteGroupNameTranslate){ + let partnames = this.namesTranslate.siteGroupNameTranslate[key2].split("_"); + if(key.startsWith(partnames[0])&&key.endsWith(partnames[1])){ + oneSiteGroup[key2] = oneSiteGroup[key]; + break; + } + } + } + }) + console.log(this.siteList) + this.drawImage(this.siteList); + } + + // site详情 + siteDetailData={baseNames:{},cpeNames:{},wanportNames:[]}; + siteDetail = false; + showSiteDetail(item){ + this.siteDetail = true; + this.siteDetailData = item; + } + + wanPortModal = false; + wanPortDetail = {}; + showWanportDetail(item){ + this.wanPortModal = true; + this.wanPortDetail = item; + } + handleCancel(){ + this.wanPortModal = false; + } + + // sitegroup详情 + + // site节点图形描绘 + // site分类,根据site查tp pnf --> allotted-resource + localSite = [];//本地site + outerSite = [];//外部site + + getSiteAResource(){ + return new Promise((res,rej)=>{ + this.detailParams.siteSer.forEach((site)=>{ + site["relationship-list"]["relationship"].find((item)=>{return item["related-to"]=="site-resource"})?this.localSite.push(site):this.outerSite.push(site); + }) + + if(this.localSite[0]["service-instance-name"].startsWith("Dc")){ + this.localSite.reverse(); + } + + if(this.outerSite[0]["service-instance-name"].startsWith("Dc")){ + this.outerSite.reverse(); + } + + // 本地site获取tp pnf + this.localSite.forEach((site)=>{ + let obj = { + customerId: this.detailParams.customer.id, + serviceType: this.detailParams.serviceType, + serviceId: site["service-instance-id"] + } + this.myhttp.getAllottedResource(obj) + .subscribe((data)=>{ + // console.log(data); + let resource = data["allotted-resource"].find((item)=>{ return item["allotted-resource-name"]=="sotn ar"}); + // console.log(resource); + let tps_pnfs = resource["relationship-list"]["relationship"].find((item)=>{ return item["related-to"]=="p-interface"})["relationship-data"]; + // console.log(tps_pnfs); + // site.pnfname = tps_pnfs.find((item)=>{return item["relationship-key"]=="pnf.pnf-name"})["relationship-value"]; + site.tpsitename = tps_pnfs.find((item)=>{return item["relationship-key"]=="p-interface.interface-name"})["relationship-value"]; + // // 通过pnfname获取domain(network-resource); + // this.myhttp.getPnfDetail(site.pnfname) + // .subscribe((data2)=>{ + // // console.log(data2); + // let networkRelation = data2["relationship-list"]["relationship"].find((item)=>{ return item["related-to"]=="network-resource"})["relationship-data"]; + // site.domain = networkRelation.find((item)=>{return item["relationship-key"]=="network-resource.network-id"})["relationship-value"]; + // res("sites-domain"); + // }) + res("sites-domain"); + }) + }) + }) + } + //通过sotn 查vpn-id --> tp pnf --> allotted-resource + relation = {sotn:{ + name:"sotn1", + vpns:[ + { + name:"vpn1", + domain:"domain1", + site:"site1", + tps:[ + {name:"tp1",lineto:"site1"}, + {name:"tp2",lineto:"domain1"} + ] + }, + { + name:"vpn2", + domain:"domain2", + site:"site2", + tps:[ + {name:"tp3",lineto:"site2"}, + {name:"tp4",lineto:"domain2"} + ] + } + ] + }}; + + vpns = [{name: "", tps: [], domain: "", sitetpname: "", othertpname: ""}]; + getSotnAresource(){ + return new Promise((res,rej)=>{ + let connectivityId = this.detailParams.sotnvpnSer["relationship-list"]["relationship"] + .find((item)=>{return item["related-to"]=="connectivity"})["relationship-data"] + .find((item2)=>{return item2["relationship-key"]=="connectivity.connectivity-id"})["relationship-value"]; + this.myhttp.getSotnConnectivity(connectivityId) + .subscribe((data)=>{ + // console.log(data); //默认一个connectivityId只能查到一个connectivity + let vpns = data.connectivity[0]["relationship-list"]["relationship"] + .filter((item)=>{ return item["related-to"]=="vpn-binding"}) + .map((item2)=>{return item2["relationship-data"].find((item3)=>{return item3["relationship-key"]=="vpn-binding.vpn-id"})["relationship-value"]}); + console.log(vpns); + this.detailParams.sotnvpnSer.vpns = vpns.map((item)=>{return {name:item}}); + this.detailParams.sotnvpnSer.vpns.forEach((vpn,index)=>{ + this.myhttp.getVpnBinding(vpn.name) + .subscribe((data2)=>{ + // console.log(data2); //默认一个vpnid只能查到一个vpnbinding + let tps_pnfs = data2["vpn-binding"][0]["relationship-list"]["relationship"] + .filter((item)=>{ return item["related-to"]=="p-interface"}) + .map((item2)=>{return item2["relationship-data"]}); + let pnfname = tps_pnfs.map((item)=>{return item.find((item2)=>{return item2["relationship-key"]=="pnf.pnf-name"})["relationship-value"]}); + let tpnames = tps_pnfs.map((item)=>{return item.find((item2)=>{return item2["relationship-key"]=="p-interface.interface-name"})["relationship-value"]}); + // console.log(pnfname) + // console.log(tpnames) + vpn.tps = tpnames; + // let thissite = this.localSite.find((item)=>{return item.pnfname == pnfname[0]}); //查找site上pnfname相同的项,即同domain + // console.log(thissite); + // thissite.tpsotnname = tpsotnnames.find((item)=>{return item!=thissite.tpsitename}); + // 通过pnfname获取domain(network-resource); + this.myhttp.getPnfDetail(pnfname[0]) + .subscribe((data2)=>{ + // console.log(data2); + let networkRelation = data2["relationship-list"]["relationship"].find((item)=>{ return item["related-to"]=="network-resource"})["relationship-data"]; + vpn.domain = networkRelation.find((item)=>{return item["relationship-key"]=="network-resource.network-id"})["relationship-value"]; + if(this.localSite[index]){ + vpn.sitetpname = this.localSite.find((site)=>{return tpnames.includes(site.tpsitename)}).tpsitename; + console.log(tpnames) + console.log(vpn.sitetpname) + vpn.othertpname = tpnames.find((name)=>{return name != vpn.sitetpname}); + }else{ + vpn.sitetpname = this.localSite[0].tpsitename; + vpn.othertpname = tpnames.find((name)=>{return name != vpn.sitetpname}); + } + + this.vpns = this.detailParams.sotnvpnSer.vpns; + res("sotn-domain"); + // console.log(vpn); + }) + + }) + }) + }) + }) + } + + drawImages(){ + + this.getSiteAResource().then((data)=>{ + console.log(data); + return this.getSotnAresource() + }).then((data)=>{ + console.log(data); + console.log(this.localSite); + this.detailSites = this.detailParams.sotnvpnSer.Type == "CCVPN"?false:true; + this.detailParams.sotnvpnSer.Type == "CCVPN"?null:this.detailLines.length = this.detailLines.length-3; + // 当只有一个vpn的时候 + if(this.detailParams.sotnvpnSer.Type == "CCVPN" && this.vpns.length == 1){ + let line = { + "x1":"32%","y1":"12%","x2":"32%","y2":"50%"//t2--site2 当本地云只有一朵的时候,tp2与本地site2相连 + } + this.detailLines.length = this.detailLines.length-6; + this.detailLines.push(line); + // 当本地site有两个的时候 + if(this.localSite.length==2){ + let line = { + "x1":"40%","y1":"52%","x2":"52%","y2":"52%"//site2--site3 + } + this.detailLines.push(line); + } + // 当外部site有两个的时候 + if(this.outerSite.length==2){ + let line = { + "x1":"75%","y1":"20%","x2":"60%","y2":"50%"//out-domain--site3 + } + this.detailLines.push(line); + } + } + }) + // let allnodes = [this.getSiteAResource(),this.getSotnAresource()]; + // Promise.all(allnodes).then((data)=>{ + // console.log(data) + // console.log(this.localSite); + + + // }) + } + + detailSites=false; + detailLines=[ //详情拓扑图连线的坐标 + { + "x1":"5%","y1":"50%","x2":"17%","y2":"25%"//site1--tp1 + }, + { + "x1":"22%","y1":"20%","x2":"17%","y2":"25%"//tp1--domian1 + } + , + { + "x1":"26%","y1":"15%","x2":"30%","y2":"12%"//domian1--tp2 + }, + + { + "x1":"80%","y1":"20%","x2":"85%","y2":"50%"//out-domain--site4 + }, + + { + "x1":"50%","y1":"22%","x2":"45%","y2":"28%"//tp4--domian2 + }, + { + "x1":"40%","y1":"11%","x2":"50%","y2":"15%"//domian2--tp3 + }, + { + "x1":"32%","y1":"11%","x2":"41%","y2":"11%"//tp2--tp3 + }, + + { + "x1":"45%","y1":"30%","x2":"35%","y2":"50%"//site2--tp4 + }, + { + "x1":"75%","y1":"20%","x2":"60%","y2":"50%"//out-domain--site3 + }, + { + "x1":"40%","y1":"52%","x2":"52%","y2":"52%"//site2--site3 + } + ]; + lines=[]; + siteImage=[]; + drawImage(sitelist){ + let cx = 200; + let cy = 200; + let r = 150; + let startAngle = -210 * (Math.PI/180); + let step = sitelist.length > 1 ? 120/(sitelist.length-1) * (Math.PI/180) : 1; + + this.lines = sitelist.map((item,index)=>{ + let x = cx + Math.cos(startAngle - step*index)*r; + let y = cy + Math.sin(startAngle - step*index)*r; + return {img:"line",x1:cx,y1:cy,x2:x,y2:y} + }) + this.siteImage = this.lines.map((item)=>{ + return {img:"site",x:item.x2 - 25,y:item.y2 - 25} + }) + console.log(this.siteImage,this.lines) + } + clickShow = false; + hoverShow = false; + toggleClick(){ + this.clickShow = !this.clickShow; + } + hoverShowcould(){ + this.hoverShow = true; + } + hoverHidecould(){ + this.hoverShow = false; + } + + + + goback(){ + this.closeDetail.emit(); + } + +} diff --git a/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.css b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.css new file mode 100644 index 00000000..2ac88d32 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.css @@ -0,0 +1,97 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.outer{ + width: 20%; + float: left; +} +.content{ + width: 100px; + margin: 30px 0; +} + +.tips{ + margin: 20px auto; + padding:0 5px; + width: 100%; + height: 40px; + line-height: 40px; + border: 1px gainsboro solid; + border-radius: 10px; +} +.submit,.delete,.add{ + padding:10px 20px; + width: 100px; + margin: 0 auto; + background: dodgerblue; + border: none; + border-radius: 10px; + color: #fff; + cursor: pointer; +} +/*.line-click{*/ + /*cursor: pointer !important;*/ +/*}*/ + + +#tpContainer{ + width:100%; + /*height: 80%;*/ + float: left; +} +.model { + padding: 15px; + height: 100%; + width: 100%; +} +.model .creation { + /*margin-top:-4%;*/ + background-color: #fff; + /*float: left;*/ + width: 20%; + position: absolute; + right: 1%; + border-radius: 5px; + box-shadow: 0 0 10px #9e9e9e; + padding: 10px; + height: 80vh; + overflow: auto; +} +.model .creation .v_color{ + height: 17px; + float: left; + margin-left: -11px; + margin-top: 5px; + border-left: 4px #3fa8eb solid; +} +.w_font4{ + font-weight: 400; +} +.title-span{ + margin-left: 10%; + font-size: 12px; +} +.red-span{ + color: red; + margin-right: 3px; +} +.choose li nz-select,.choose li input{ + display: block !important; + margin: 5px 10% 15px; + width: 80%; +} + + + diff --git a/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.html b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.html new file mode 100644 index 00000000..4fbe4875 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.html @@ -0,0 +1,135 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<div class="model"> + <!--图表--> + <div class="tips"> + 请根据以下操作,来配置您的网络 + </div> + <button nz-button nzType="primary" style="margin-top: 2px;display: block" (click)="showForm()">Add Link</button> + <div id="tpContainer" style="overflow: hidden;"></div> + <!--弹出框--> + <div class="creation" id="d3_form" *ngIf="isVisible==true"> + <span class="v_color"></span> + <h3 class="w_font4">Set Attribtes</h3> + <ul class="choose"> + <li> + <span class="title-span"><span class="red-span">*</span>Link Name </span> + <input nz-input [(ngModel)]="linkName" maxlength="20"> + </li> + </ul> + <h4>Left Port</h4> + <ul class="choose"> + <li> + <span class="title-span"><span class="red-span">*</span>Network </span> + <nz-select [(ngModel)]="networkVal1" nzShowSearch nzAllowClear (ngModelChange)="network1Change($event)"> + <nz-option *ngFor="let option of networkOption" [nzLabel]="option.network" [nzValue]="option.network"></nz-option> + </nz-select> + </li> + <li> + <span class="title-span"> <span class="red-span">*</span>Node </span> + <nz-select [(ngModel)]="selectedNode1" nzShowSearch nzAllowClear (ngModelChange)="node1Change($event)"> + <nz-option *ngFor="let node of nodeOption1[networkVal1]" [nzValue]="node" [nzLabel]="node"></nz-option> + </nz-select> + </li> + <li> + <span class="title-span"><span class="red-span">*</span>Terminal Point </span> + <!-- <input nz-input id="city-one" value=""> --> + <nz-select [(ngModel)]="selecteTpName1" nzShowSearch nzAllowClear> + <nz-option *ngFor="let tp of tpOption1" [nzValue]="tp" [nzLabel]="tp"></nz-option> + </nz-select> + </li> + </ul> + <h4>Right Port</h4> + <label nz-checkbox [(ngModel)]="inputshow">Partner Network</label> + <ul class="choose"> + <li> + <span class="title-span"><span *ngIf="inputshow" class="red-span">*</span>Host Url</span> + <input nz-input [(ngModel)]="cloudUrl" [disabled]='!inputshow' [attr.disabled] ='!inputshow?true:undefined'> + </li> + <li> + <span class="title-span"><span class="red-span">*</span>Network </span> + <input nz-input *ngIf="inputshow" [(ngModel)]="cloudNetwork"> + <nz-select *ngIf="!inputshow" [(ngModel)]="networkVal2" nzShowSearch nzAllowClear (ngModelChange)="network2Change($event)"> + <nz-option *ngFor="let option of networkOption" [nzLabel]="option.network" [nzValue]="option.network"> </nz-option> + </nz-select> + </li> + <li> + <span class="title-span"><span class="red-span">*</span>Node </span> + <input nz-input *ngIf="inputshow" [(ngModel)]="cloudNode"> + <nz-select *ngIf="!inputshow" [(ngModel)]="selectedNode2" nzShowSearch nzAllowClear (ngModelChange)="node2Change($event)"> + <nz-option *ngFor="let node of nodeOption1[networkVal2]" [nzValue]="node" [nzLabel]="node"></nz-option> + </nz-select> + </li> + <li> + <span class="title-span"><span class="red-span">*</span>Terminal Point </span> + <input nz-input *ngIf="inputshow" [(ngModel)]="cloudTp"> + <nz-select *ngIf="!inputshow" [(ngModel)]="selecteTpName2" nzShowSearch nzAllowClear> + <nz-option *ngFor="let tp of tpOption2" [nzValue]="tp" [nzLabel]="tp"></nz-option> + </nz-select> + </li> + </ul> + <button nz-button nzType="primary" nzSize="small" style="width: 60px;" (click)="submitForm()">OK</button> + <button nz-button nzType="default" nzSize="small" style="width: 60px;" (click)="hideForm()">Cancel</button> + </div> + <div class="creation" id="delbox" *ngIf="delBoxisVisible==true"> + <span class="v_color"></span> + <h3 class="w_font4">Set Attribtes</h3> + <ul class="choose"> + <li> + <span class="title-span">Link Name </span> + <input nz-input [(ngModel)]="delLinkname" disabled="disabled"> + </li> + </ul> + <h4>Left Port</h4> + <ul class="choose"> + <li> + <span class="title-span">Network </span> + <input nz-input [(ngModel)]="delNetwork1" disabled="disabled"> + </li> + <li> + <span class="title-span">Node </span> + <input nz-input [(ngModel)]="delNode1" disabled="disabled"> + </li> + <li> + <span class="title-span">Terminal Point </span> + <input nz-input [(ngModel)]="delTp1" disabled="disabled"> + </li> + </ul> + <h4>Right Port</h4> + <ul class="choose"> + <li *ngIf="delcloud"> + <span class="title-span">Host Url</span> + <input nz-input [(ngModel)]="delcloudUrl" disabled="disabled"> + </li> + <li> + <span class="title-span">Network </span> + <input nz-input [(ngModel)]="delNetwork2" disabled="disabled"> + </li> + <li> + <span class="title-span">Node </span> + <input nz-input [(ngModel)]="delNode2" disabled="disabled"> + </li> + <li> + <span class="title-span">Terminal Point </span> + <input nz-input [(ngModel)]="delTp2" disabled="disabled"> + </li> + </ul> + <button nz-button nzType="primary" nzSize="small" class="del-button" style="width: 90px;" (click)="delLink()" *ngIf="!delcloud">delete Link</button> + <button nz-button nzType="primary" nzSize="small" class="del-button" style="width: 90px;" (click)="delCloudLink()" *ngIf="delcloud">delete Link</button> + <button nz-button nzType="default" nzSize="small" class="del-button" style="width: 60px;" (click)="hideForm()">Cancel</button> + </div> +</div> + diff --git a/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.spec.ts b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.spec.ts new file mode 100644 index 00000000..03cc5065 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.spec.ts @@ -0,0 +1,40 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CcvpnNetworkComponent } from './ccvpn-network.component'; + +describe('CcvpnNetworkComponent', () => { + let component: CcvpnNetworkComponent; + let fixture: ComponentFixture<CcvpnNetworkComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CcvpnNetworkComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CcvpnNetworkComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.ts b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.ts new file mode 100644 index 00000000..0174aa77 --- /dev/null +++ b/usecaseui-portal/src/app/ccvpn-network/ccvpn-network.component.ts @@ -0,0 +1,1195 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import {Component, EventEmitter, OnInit, Output} from '@angular/core'; +import * as d3 from 'd3'; +import * as $ from 'jquery'; +import {networkHttpservice} from '../networkHttpservice.service'; + +@Component({ + selector: 'app-ccvpn-network', + templateUrl: './ccvpn-network.component.html', + styleUrls: ['./ccvpn-network.component.css'] +}) +export class CcvpnNetworkComponent implements OnInit { + + constructor(private myhttp: networkHttpservice) { + } + + ngOnInit() { + var thisNg = this; + thisNg.getD3Data(); + + + //本地云TP端口断开连线 ,直接点击线可删除连线 + $('#tpContainer').on('click', '.line-local', function () { + thisNg.isVisible = false; + thisNg.delBoxisVisible = true; + thisNg.delcloud = false; + + thisNg.delTp1 = $(this).attr('data-tp1'); + thisNg.delTp2 = $(this).attr('data-tp2'); + thisNg.delNode1 = $(this).attr('data-node1'); + thisNg.delNode2 = $(this).attr('data-node2'); + thisNg.delVersion = $(this).attr('data-version'); + thisNg.delLinkname = $(this).attr('data-link'); + + thisNg.delLinkIndex = $(this); + let dataD3 = thisNg.d3Data; + for (let p = 0; p < dataD3.length; p++) {//判断两个tp端口分别属于哪个Domain network + if (dataD3[p]['name'] == thisNg.delTp1) { + thisNg.network.push(dataD3[p]['source']['name']); + } + if (dataD3[p]['name'] == thisNg.delTp2) { + thisNg.network.push(dataD3[p]['source']['name']); + } + } + thisNg.delNetwork1 = thisNg.network[0]; + thisNg.delNetwork2 = thisNg.network[1]; + }); + + //外部云 断开连线 ,直接点击线可删除连线 + $('#tpContainer').on('click', '.cloudline', function () { + thisNg.isVisible = false; + thisNg.delBoxisVisible = true; + thisNg.delcloud = true; + + thisNg.delTp1 = $(this).attr('data-tp1'); + thisNg.delTp2 = $(this).attr('data-tp2'); + thisNg.delNode1 = $(this).attr('data-node1'); + thisNg.delNode2 = $(this).attr('data-node2'); + thisNg.delVersion = $(this).attr('data-version'); + thisNg.delNetwork1 = $(this).attr('data-network'); + thisNg.delNetwork2 =$(this).attr('data-cloudnetwork'); + thisNg.delcloudUrl =$(this).attr('data-url'); + thisNg.delLinkname = $(this).attr('data-link'); + thisNg.aaiId =$(this).attr('data-aaiid'); + thisNg.getCloudUrl(thisNg.aaiId,thisNg); + }); + } + + addLinkDisabled = true; + isVisible = false; + outCloudShow = false; + inputshow = false; + delBoxisVisible = false; + + d3Data = [];//D3渲染需要的数据 + logicalLinks = [];//logicalLinks接口返回的已有的连线数据 + linkName=null;//连线的名字link-name + networkOption = [];//表单network下拉选框填充的数据 + nodeOption1 = {};//node下拉选框填充的数据 + tpOption1 = [];//node下拉选框填充的数据 + tpOption2 = [];//node下拉选框填充的数据 + networkVal1 = null;//network1下拉框默认数据 + networkVal2 = null;//network2下拉框默认数据 + selectedNode1 = null;//node1下拉框默认数据 + selectedNode2 = null;//node2下拉框默认数据 + selecteTpName1 = null;//TP1下拉框默认数据 + selecteTpName2 = null;//TP2下拉框默认数据 + cloudUrl = null;//外部云URL地址 + cloudNetwork = null;//外部云network名称 + cloudNode = null;//外部云Node名称 + cloudTp = null;//外部云Tp名称 + dataCloud=[];//外部云的信息 + dataCloudLink=[]; + aaiId=""; + charge=-200; + + //删除连线时 右侧框显示的数据 + delLinkname=null; + delNetwork1 = null; + delNode1 = null; + delTp1 = null; + delcloudUrl = null; + delNetwork2 = null; + delNode2 = null; + delTp2 = null; + delVersion = null; + delLinkIndex = null; + network = []; + delcloud = false; + + winWidth = $('.content').width(); + winHeight = $('.content').height(); + + + imgmap = { + '1': '../assets/images/cloud-county1.png', + '2': '../assets/images/cloud-city1.png', + '3': '../assets/images/cloud-out.png', + }; + tpoption = { + container: '#tpContainer', + data: '', + width: 1000, + height: this.winHeight + }; + + showForm(): void { + if (this.addLinkDisabled == false) { + this.isVisible = true; + this.delBoxisVisible = false; + } + } + + hideForm(): void { + this.isVisible = false; + this.delBoxisVisible = false; + this.linkName=null; + this.networkVal1 = null;//初始化network1下拉框默认数据 + this.networkVal2 = null;//初始化network2下拉框默认数据 + this.selectedNode1 = null;//初始化node1下拉框默认数据 + this.selectedNode2 = null;//初始化node2下拉框默认数据 + this.selecteTpName1 = null;//初始化TP1下拉框默认数据 + this.selecteTpName2 = null;//初始化TP2下拉框默认数据 + // this.localUrl=null;//本地云URL地址 + this.cloudUrl = null;//外部云URL地址 + this.cloudNetwork = null;//外部云network名称 + this.cloudNode = null;//外部云Node名称 + this.cloudTp = null;//外部云Tp名称 + } + + + tpName=null; + tpNameStyle = { + 'display':'none', + 'left':'0', + 'top':'0' + }; + showtp($event,item){ + console.log(111111111) + this.tpName = item; + this.tpNameStyle.display = 'block'; + } + movetp($event,item){ + this.tpNameStyle.left = $event.clientX + "px"; + this.tpNameStyle.top = $event.clientY - 35 + "px"; + } + hidetp($event){ + this.tpNameStyle.display = 'none'; + } + + //获取云图数据 + getD3Data() { + this.myhttp.getNetworkD3Data() + .subscribe((data) => { + if(data.length==0){ + this.addLinkDisabled = false; + return; + }; + for(let ii=0;ii<data.length;ii++){ + if(data[ii]["aaiId"]!=""){ + this.dataCloud=data.splice(ii,1) + } + } + for (var i = 0; i < data.length; i++) { + let name1 = {}, name2 = {}; + let nodess = []; + name1['name'] = name2['network'] = data[i]['networkId']; + name1['type'] = '1'; + name1['source'] = i; + this.d3Data.push(name1); + for (let c = 0; c < data[i]["pnfs"].length; c++) { + nodess.push(data[i]['pnfs'][c]['pnfName']); + this.nodeOption1[name2['network']] = nodess; + } + this.networkOption.push(name2); + } + console.log(this.networkOption) + for (var i = 0; i < data.length; i++) { + let tp_length = data[i]['tps'].length; + for (var h = 0; h < tp_length; h++) { + let name2 = {}; + let interface_name = data[i]['tps'][h]['interface-name']; + name2['name'] = interface_name; + name2['type'] = '2'; + name2['source'] = i; + this.d3Data.push(name2); + } + } + for (let b = 0; b < this.d3Data.length; b++) { + this.d3Data[b]['target'] = b; + } + this.initPosition(this.d3Data); + setTimeout(this.render(this.d3Data, this.imgmap,this.dataCloud,this.charge,data),0) + }, (err) => { + console.log(err); + }); + + } + + //获取云图初始的连线状态 getlogicalLinksData + getLinksData() { + this.myhttp.getLogicalLinksData() + .subscribe((data) => { + console.log(data["status"]) + if (data["status"]=="FAILED") { + return; + } + for (let i = 0; i < data["logical-link"].length; i++) { + if(data["logical-link"][i]["relationship-list"]["relationship"].length>2){ + this.dataCloudLink=data["logical-link"].splice(i,1); + } + } + console.log(this.dataCloudLink) + for (let i = 0; i < data["logical-link"].length; i++) { + let textval = []; + textval[0] = data['logical-link'][i]['relationship-list']['relationship'][0]['relationship-data'][1]['relationship-value'];//tp1 + textval[1] = data['logical-link'][i]['relationship-list']['relationship'][1]['relationship-data'][1]['relationship-value'];//tp2 + textval[2] = data['logical-link'][i]['resource-version'];//version + textval[3] = data['logical-link'][i]['relationship-list']['relationship'][0]['relationship-data'][0]['relationship-value'];//node1 + textval[4] = data['logical-link'][i]['relationship-list']['relationship'][1]['relationship-data'][0]['relationship-value'];//node2 + textval[5] = data['logical-link'][i]['operational-status']; + textval[6] = data['logical-link'][i]['link-name']; + this.logicalLinks.push(textval); + this.chose(textval); + } + if(this.dataCloudLink.length>0){ + this.getcloudLine(this.dataCloudLink) + } + }, (err) => { + console.log(err); + }); + } + + //D3云图渲染 + render(nodes, imgmap,dataCloud,charge,dataD3) { + var thiss = this; + var _this = this.tpoption, + width = null, + height = _this.height; + if (_this.width > 800) { + width = _this.width; + } else { + width = 800; + } + + var str=""; + for(var i=0;i<10;i++){ + str+="<div>这是div"+i+"</div>" + } + + if(dataD3.length<=4){ + charge=-850; + }else if(dataD3.length>4 && dataD3.length<=6) { + charge=-700; + }else if(dataD3.length>6 && dataD3.length<=10) { + charge=-600; + }else { + charge=-150; + } + var svg = d3.select(_this.container).append('svg') + .attr('width', width) + .attr('height', height) + .attr('id', 'content-svg') + .style('pointer-events', 'all'), + graph = svg.append('g').attr('class', 'graph').attr('id', 'graph'), + + _g_nodes = graph.selectAll('g.node') + .data(nodes) + .enter() + .append('g') + .style('display', function (d) { + var display = 'block'; + switch (d.type) { + case '1': + display = 'none'; + break; + case '2': + display = 'none'; + break; + default: + break; + } + return display; + }) + .style('cursor', 'pointer') + .attr('class', 'node'), + + _g_lines = graph.selectAll('line.line') + .data(nodes) + .enter() + .append('g') + .style('display', 'none') + .attr('class', 'line'); + + + _g_lines.append('line') + .style('stroke', '#93c62d' + ) + .style('stroke-width', 2); + + _g_nodes.append('image') + .attr('width', function (d) { + var width = 40; + switch (d.type) { + case '1': + width = 4.4 * width; + break; + case '2': + width = 0.12 * width; + break; + default: + break; + } + return width; + }) + .attr('height', function (d) { + var height = 20; + switch (d.type) { + case '1': + height = 3.5 * height; + break; + case '2': + height = 0.2 * height; + break; + default: + break; + } + return height; + }) + .attr('xlink:href', function (d) { + return imgmap[d.type]; + }); + + _g_nodes.append('text') + .text(function (d) { + return d.name; + }) + .style('transform', function (d) { + var x = null; + var y = null; + switch (d.type) { + case '1': + x = 7; + y = -7; + break; + case '2': + x = 1; + y = -2; + break; + default: + break; + } + return 'translate(' + x + '%,' + y + '%)'; + }) + .style('font-size', function (d) { + var size = 14; + switch (d.type) { + case '1': + size = 14; + break; + case '2': + size = 12; + break; + default: + break; + } + return size; + }) + .style('fill', function (d) { + var color = '#666'; + switch (d.type) { + case '1': + color = '#666'; + break; + case '2': + color = '#666'; + break; + default: + break; + } + return color; + }) + .style('font-weight', '500'); + + + //线上添加自定义属性 + _g_lines.each(function (d, i) { + var _this = d3.select(this); + if (d.name) { + _this.attr('data-text', d.name); + } + }); + var force = d3.layout.force() + .size([1000,this.winHeight]) + .linkDistance(5) + // .theta(0) + .charge(charge) + .nodes(nodes) + .links(nodes) + .start(); + // let distanceMax2=1; + // force.distanceMax = function(_) { + // return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); + // }; + //添加拖拽行为 + // _g_nodes.call(this.getDragBehavior(force)); + + + force.on('tick', function (d) { + nodes.forEach(function(d,i){ + + d.x = d.x - 25 < 0 ? 25 : d.x ; + d.x = d.x + 25 > width ? width - 25 : d.x ; + d.y = d.y - 15 < 0 ? 15 : d.y ; + d.y = d.y + 15> height ? height - 15 : d.y ; + }); + if(force.alpha()<=0.1){ + + + _g_nodes.style('display', function (d) { + + + var display = 'block'; + switch (d.type) { + case '1': + display = 'block'; + break; + case '2': + display = 'none'; + break; + default: + break; + } + return display; + }); + + _g_lines.select('line') + .attr('x1', function (d) { + return d.source.x; + }) + .attr('y1', function (d) { + return d.source.y; + }) + .attr('x2', function (d) { + return d.target.x; + }) + .attr('y2', function (d) { + return d.target.y; + }); + + _g_nodes.attr('transform', function (d) { + // console.log(d) + // if(d["type"]==1){ + // d["x"]=400; + // d["y"]=400; + // }else { + // d["x"]=d["x"]; + // d["y"]=d["y"] + // } + var image = d3.select(this).select('image')[0][0], + halfWidth = parseFloat(image.attributes[0]['value']) / 2, + halfHeight = parseFloat(image.attributes[1]['value']) / 2; + + + return 'translate(' + (d.x - halfWidth) + ',' + (d.y - halfHeight) + ')'; + }); + + _g_nodes.select('text').attr('dy', function (d) { + var image = this.previousSibling, + height = parseFloat(image.attributes[1]['value']), + fontSize = 12; + return height + 1.5 * fontSize; + }); + + + } + + }); + + force.on('end', function () { + + force.stop(); + if(dataCloud.length>0){ + thiss.getoutCloud(dataCloud,imgmap); + } + thiss.getLinksData(); + thiss.addLinkDisabled = false; + }); + + }; + + //拓扑图拖拽效果 + getDragBehavior(force) { + + return d3.behavior.drag() + .origin(function (d) { + return d; + }) + .on('dragstart', dragstart) + .on('drag', dragging) + .on('dragend', dragend); + + function dragstart(d) { + d3.event.sourceEvent.stopPropagation(); + d3.select(this).classed('dragging', true); + force.start(); + } + + function dragging(d) { + d.x = d3.event.x; + d.y = d3.event.y; + } + + function dragend(d) { + d3.select(this).classed('dragging', false); + } + + } + + //初始化节点位置 + initPosition(datas) { + let origin = [this.tpoption.width / 2, this.tpoption.height / 2]; + let points = this.getVertices(origin, Math.min(this.tpoption.width/2, this.tpoption.height/2), datas.length); + datas.forEach((item, i) => { + item.x = points[i].x; + item.y = points[i].y; + }); + } + + //根据多边形获取定位点 + getVertices(origin, r, n) { + if (typeof n !== 'number') return; + var ox = origin[0]; + var oy = origin[1]; + var angle = 30 * n / n; + var i = 0; + var points = []; + var tempAngle = 0; + while (i < n) { + tempAngle = (i * angle * Math.PI) / 180; + points.push({ + x: ox - r * Math.sin(tempAngle), + y: oy - r * Math.cos(tempAngle), + }); + i++; + } + return points; + } + + //渲染外部云 + getoutCloud(dataCloud,imgmap) { + var _this = this, + width; + let networkId=dataCloud[0]["networkId"]; + if (_this.tpoption.width > 800) { + width = _this.tpoption.width; + } else { + width = 800; + } + var svg = d3.select('#content-svg'); + svg.append('g').attr('class', 'out').attr('id', 'out').style({'display': 'block'}).attr('transform', 'translate(' + (width - 200) + ',0)'); + var out = d3.select('#out'); + out.append('image').style('width', '200').style('height', '118').attr('xlink:href', imgmap['3']); + out.append('text').text(networkId) + .style('transform', 'translate(0,0)') + .style('font-size', '16') + .style('font-weight', '400') + .attr('dx', '40') + .attr('dy', '70') + .style('fill', '#666'); + } + + //外部云连接 + getcloudLine(dataCloudLink) { + let textval = []; + textval[0] = dataCloudLink[0]['relationship-list']['relationship'][0]['relationship-data'][1]['relationship-value'];//tp1 + textval[1] = dataCloudLink[0]['relationship-list']['relationship'][1]['relationship-data'][1]['relationship-value'];//tp2 + textval[2] = dataCloudLink[0]['resource-version'];//version + textval[3] = dataCloudLink[0]['relationship-list']['relationship'][0]['relationship-data'][0]['relationship-value'];//node1 + textval[4] = dataCloudLink[0]['relationship-list']['relationship'][1]['relationship-data'][0]['relationship-value'];//node2 + textval[5] = dataCloudLink[0]['operational-status'];//status + textval[6] = dataCloudLink[0]['relationship-list']['relationship'][2]['relationship-data'][0]['relationship-value'];//aaiId + textval[7] =this.dataCloud[0]["networkId"]; + console.log(this.dataCloud); + let dataD3=this.d3Data; + for (let p = 0; p < dataD3.length; p++) {//判断两个tp端口分别属于哪个Domain network + if (dataD3[p]['name'] == textval[0]) { + textval[8] =dataD3[p]['source']['name'];//network1 + } + } + textval[9] =dataCloudLink[0]["link-name"]; + + let lines_json = {}; + var _this = this, + width; + if (_this.tpoption.width > 800) { + width = _this.tpoption.width; + } else { + width = 800; + } + for (let i = 0; i < $(".node").length; i++) { + if ($('.node').eq(i).find('text').html() == textval[0]) { + //获取二级的x,y坐标 + $('.node').eq(i).show(); + var translates = $('.node').eq(i).css('transform'); + lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); + lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); + lines_json['x2'] = width - 100; + lines_json['y2'] = 100; + } + } + var x1 = lines_json['x1']; + var y1 = lines_json['y1']; + var x2 = lines_json['x2']; + var y2 = lines_json['y2']; + var color='#14bb58'; + if(textval[5]=="up"){ + color='#14bb58'; + }else { + color='red'; + } + var line = "<line class='line cloudline ' stroke='"+color+"' stroke-width='2' style='cursor:pointer'></line>"; + var svg = d3.select('#graph'); + $(".cloudline").remove(); + $('#graph').prepend(line); + $('.cloudline').attr({ + x1: x1 + 100, + y1: y1 + 10, + x2: x2, + y2: y2, + 'data-tp1': textval[0], + 'data-tp2': textval[1], + 'data-version': textval[2], + 'data-node1':textval[3], + 'data-node2':textval[4], + 'data-network':textval[8], + 'data-cloudnetwork':textval[7], + 'data-url':"", + 'data-aaiid':textval[6], + "data-link":textval[9] + }); + svg.html(svg.html()); + } + + //查询外部云host url地址 + getCloudUrl(aaiId,thisNg){ + this.myhttp.queryCloudUrl(aaiId) + .subscribe((data) => { + thisNg.delcloudUrl=data["service-url"]; + }, (err) => { + console.log(err); + }); + } + + + //右侧表单下拉选框数据填充 三级联动 + //Left Port + network1Change(value: string): void { + this.selectedNode1 = this.nodeOption1[value][0]; + this.getPInterfaces1(); + } + + node1Change(): void { + this.getPInterfaces1(); + } + + //获取指定node下的TP数据 + getPInterfaces1() { + let params = { + pnfName: this.selectedNode1, + }; + this.myhttp.getPInterfacesData1(params) + .subscribe((data) => { + this.tpOption1 = []; + for (let i = 0; i < data.length; i++) { + let tpName = data[i]['interface-name']; + this.tpOption1.push(tpName); + } + this.selecteTpName1 = this.tpOption1[0]; + }, (err) => { + // console.log(err); + }); + } + + //Right Port + network2Change(value: string): void { + this.selectedNode2 = this.nodeOption1[value][0]; + this.getPInterfaces2(); + } + + node2Change(): void { + this.getPInterfaces2(); + } + + //获取指定node下的TP数据 + getPInterfaces2() { + let params = { + pnfName: this.selectedNode2, + }; + this.myhttp.getPInterfacesData2(params) + .subscribe((data) => { + this.tpOption2 = []; + for (let i = 0; i < data.length; i++) { + let tpName = data[i]['interface-name']; + this.tpOption2.push(tpName); + } + this.selecteTpName2 = this.tpOption2[0]; + }, (err) => { + // console.log(err); + }); + } + + //提交表单,连线 + submitForm(): void { + //当页面ONAP未选中,即本地云端TP连线 + var _thiss = this; + if (this.inputshow == false) { + if (this.linkName == null || this.networkVal1 == null || this.selectedNode1 == null || this.selecteTpName1 == null || this.networkVal2 == null || this.selectedNode2 == null || this.selecteTpName2 == null) { + alert('服务端口不能为空,请选择端口信息'); + return; + } else if (this.networkVal1 == this.networkVal2) { + alert('同一云服务下的TP端口不能相连!'); + return; + } + let tp_links = [], + tp1 = this.selecteTpName1, + tp2 = this.selecteTpName2; + for (let i = 0; i < $(".line-local").length; i++) { + let data_text1 = $('.line-local').eq(i).attr('data-tp1'); + let data_text2 = $('.line-local').eq(i).attr('data-tp2'); + tp_links.push(data_text1); + tp_links.push(data_text2); + } + if (tp_links.indexOf(tp1) != -1 || tp_links.indexOf(tp2) != -1) { + alert('此端口号连线已存在!'); + return; + } + this.createTpLinks(); + + } else { + //当页面ONAP选中,即创建外部云,连线 + if (this.linkName == null || this.networkVal1 == null || this.selectedNode1 == null || this.selecteTpName1 == null || this.cloudUrl == null || this.cloudNetwork == null || this.cloudNode == null || this.cloudTp == null) { + alert('服务端口信息不能为空,请填写完整的端口信息'); + return; + } + let tp_links = [], + tp1 = this.selecteTpName1; + for (let i = 0; i < $(".line-local").length; i++) { + let data_text1 = $('.line-local').eq(i).attr('data-tp1'); + tp_links.push(data_text1); + } + if (tp_links.indexOf(tp1) != -1) { + alert('此端口号连线已存在!'); + return; + } + Promise + .all([this.createCloudNetwork(), this.createPnfs(), this.createCloudTp(), this.createCloudLinks()]) + .then(function (results) { + console.log(results); + if (results.indexOf('FAIL') == -1) { + // _thiss.queryOutCloudLink(); + _thiss.outCloudShow = true; + _thiss.outCloud(_thiss.imgmap); + setTimeout(_thiss.cloudLine(_thiss.networkVal1, _thiss.selectedNode1, _thiss.selecteTpName1, _thiss.cloudUrl, _thiss.cloudNetwork, _thiss.cloudNode, _thiss.cloudTp, 121211,"up",_thiss.linkName), 0); + _thiss.hideForm(); + } else { + console.log('失败'); + } + }); + + } + } + + //创建tp连线 调用接口createLink + createTpLinks() { + let params = { + 'link-name': this.linkName, + 'in-maint': false, + 'link-type': 'cross-link', + 'speed-value': '10000', + 'operational-status': 'up', + 'relationship-list': { + 'relationship': [ + { + 'related-to': this.selecteTpName1, + 'related-link': '/aai/v13/network/pnfs/pnf/' + this.selectedNode1 + '/p-interfaces/p-interface/' + this.selecteTpName1 + }, + { + 'related-to': this.selecteTpName2, + 'related-link': '/aai/v13/network/pnfs/pnf/' + this.selectedNode2 + '/p-interfaces/p-interface/' + this.selecteTpName2 + } + ] + } + }; + this.myhttp.createLink(params) + .subscribe((data) => { + if (data["status"] == 'SUCCESS') { + this.queryAddLink(); + } + }, (err) => { + // console.log(err); + alert('系统忙,连接失败!'); + }); + } + + //创建tp连接线后马上查询新增的连线 + queryAddLink() { + let linkName=this.linkName, + selecteTpName1 = this.selecteTpName1, + selecteTpName2 = this.selecteTpName2, + selectedNode1 = this.selectedNode1, + selectedNode2 = this.selectedNode2; + let params = { + 'link-name': selecteTpName1 + '_' + selecteTpName2, + }; + this.myhttp.querySpecificLinkInfo(params) + .subscribe((data) => { + let version = data['resource-version'], + operational_status = data['operational-status']; + let textval = [selecteTpName1, selecteTpName2, version, selectedNode1, selectedNode2, operational_status,linkName]; + this.hideForm(); + this.chose(textval); + }, (err) => { + // console.log(err); + alert('系统忙,连接失败!'); + }); + } + + //两个TP之间的连线 坐标获取 + chose(textval) { + var lines_json = {}; + lines_json['tp1'] = textval[0]; + lines_json['tp2'] = textval[1]; + lines_json['version'] = textval[2]; + lines_json['node1'] = textval[3]; + lines_json['node2'] = textval[4]; + lines_json['status'] = textval[5]; + lines_json['linkname'] = textval[6]; + for (let i = 0; i < $(".node").length; i++) { + if ($('.node').eq(i).find('text').html() == textval[0]) { + $('.node').eq(i).show(); + //获取二级的x,y坐标 + var translates = $('.node').eq(i).css('transform'); + lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); + lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); + } + if ($('.node').eq(i).find('text').html() == textval[1]) { + $('.node').eq(i).show(); + var translates = $('.node').eq(i).css('transform'); + lines_json['x2'] = parseFloat(translates.substring(7).split(',')[4]); + lines_json['y2'] = parseFloat(translates.substring(7).split(',')[5]); + } + } + + this.addLine(lines_json); + } + + //两个TP之间的连线 连线渲染 + addLine(lines) { + let tp1 = lines.tp1; + let tp2 = lines.tp2; + let version = lines.version; + let node1 = lines.node1; + let node2 = lines.node2; + let status = lines.status; + let linkname = lines.linkname; + let x1 = lines.x1; + let y1 = lines.y1 + 5; + let x2 = lines.x2; + let y2 = lines.y2 + 5; + let color = '#14bb58'; + if (status == 'up') { + color = '#14bb58'; + } else { + color = 'red'; + } + let line = '<line class=\'line line-local \' stroke=\'' + color + '\' stroke-width=\'2\' style=\'cursor:pointer\'></line>'; + let svg = d3.select('#graph'); + $('#graph').prepend(line); + $('.line').first().attr({ + x1: x1, + y1: y1, + x2: x2, + y2: y2, + 'data-tp1': tp1, + 'data-tp2': tp2, + 'data-version': version, + 'data-node1': node1, + 'data-node2': node2, + "data-link":linkname + }); + svg.html(svg.html()); + } + + //创建外部云连线后,马上查询连线 + queryOutCloudLink() { + let networkVal1 = this.networkVal1, + selectedNode1 = this.selectedNode1, + selecteTpName1 = this.selecteTpName1, + cloudUrl = this.cloudUrl, + cloudNetWork = this.cloudNetwork, + cloudNode = this.cloudNode, + cloudTp = this.cloudTp, + linkname=this.linkName; + let params = { + 'link-name': linkname, + }; + this.myhttp.querySpecificLinkInfo(params) + .subscribe((data) => { + let version = data['resource-version']; + let status = data['operational-status']; + let link_name = data['link-name']; + this.outCloudShow = true; + this.outCloud(this.imgmap); + setTimeout(this.cloudLine(networkVal1, selectedNode1, selecteTpName1, cloudUrl, cloudNetWork, cloudNode, cloudTp, version,status,link_name), 0); + }, (err) => { + // console.log(err); + alert('系统忙,连接失败!'); + }); + } + + //新增外部云 + outCloud(imgmap) { + var _this = this, + width; + if (_this.tpoption.width > 800) { + width = _this.tpoption.width; + } else { + width = 800; + } + var svg = d3.select('#content-svg'); + svg.append('g').attr('class', 'out').attr('id', 'out').style({'display': 'block'}).attr('transform', 'translate(' + (width - 200) + ',0)'); + var out = d3.select('#out'); + out.append('image').style('width', '200').style('height', '118').attr('xlink:href', imgmap['3']); + out.append('text').text('Partner Network') + .style('transform', 'translate(0,0)') + .style('font-size', '16') + .style('font-weight', 'bold') + .attr('dx', '40') + .attr('dy', '70') + .style('fill', '#fff'); + } + + //新增 外部云连接 + cloudLine(networkVal1, selectedNode1, selecteTpName1, cloudUrl, cloudNetWork, cloudNode, cloudTp, version,status,link_name) { + let lines_json = {}; + var _this = this, + width; + if (_this.tpoption.width > 800) { + width = _this.tpoption.width; + } else { + width = 800; + } + for (let i = 0; i < $(".node").length; i++) { + if ($('.node').eq(i).find('text').html() == networkVal1) { + //获取二级的x,y坐标 + var translates = $('.node').eq(i).css('transform'); + lines_json['x1'] = parseFloat(translates.substring(7).split(',')[4]); + lines_json['y1'] = parseFloat(translates.substring(7).split(',')[5]); + lines_json['x2'] = width - 100; + lines_json['y2'] = 100; + } + } + var x1 = lines_json['x1']; + var y1 = lines_json['y1']; + var x2 = lines_json['x2']; + var y2 = lines_json['y2']; + var color='#14bb58'; + if(status=="up"){ + color='#14bb58'; + }else { + color='red'; + } + var line = "<line class='line cloudline ' stroke='"+color+"' stroke-width='2' style='cursor:pointer'></line>"; + var svg = d3.select('#graph'); + $(".cloudline").remove(); + $('#graph').prepend(line); + $('.cloudline').attr({ + x1: x1 + 100, + y1: y1 + 10, + x2: x2, + y2: y2, + 'data-tp1': selecteTpName1, + 'data-tp2': cloudTp, + 'data-version': version, + 'data-node1':selectedNode1, + 'data-node2':cloudNode, + 'data-network':networkVal1, + 'data-cloudnetwork':cloudNetWork, + 'data-url':cloudUrl, + "data-link":link_name + }); + svg.html(svg.html()); + } + + //创建外部云,连线时调用以下4个接口:createCloudNetwork,createPnfs,createCloudTp,createCloudLinks + createCloudNetwork() { + let _thiss = this; + let params = { + 'selflink': this.cloudUrl, + 'network-id': this.cloudNetwork, + 'provider-id': '', + 'client-id': '', + 'te-topo-id': '' + }; + var pro = new Promise(function (resolve, reject) { + //做一些异步操作 + _thiss.myhttp.createNetwrok(params) + .subscribe((data) => { + resolve(data["status"]); + }, (err) => { + console.log(err); + }); + }); + return pro; + } + + createPnfs() { + let _thiss = this; + let params= { + "pnf-name": this.cloudNode, + "pnf-id": "", + "in-maint": "", + "admin-status": "up", + "operational-status": "up", + "relationship-list": { + "relationship": { + "related-to": "network-resource", + "relationship-label": "tosca.relationships.network.LinksTo", + "related-link": "/aai/v13/network/network-resources/network-resource/"+this.cloudNetwork, + "relationship-data": { + "relationship-key": "network-resource.network-id", + "relationship-value": this.cloudNetwork + } + } + } + }; + var pro = new Promise(function (resolve, reject) { + //做一些异步操作 + _thiss.myhttp.createNetwrok(params) + .subscribe((data) => { + resolve(data["status"]); + }, (err) => { + console.log(err); + }); + }); + return pro; + } + + createCloudTp() { + let _thiss = this; + let params= { + "interface-name": this.cloudTp, + "speed-value": "100000", + "in-maint": "true", + "network-ref": "", + "transparent": "", + "operational-status": "up", + }; + let cloudNodeName=this.cloudNode; + + var pro = new Promise(function (resolve, reject) { + //做一些异步操作 + _thiss.myhttp.createTp(params,cloudNodeName) + .subscribe((data) => { + resolve(data["status"]); + }, (err) => { + // console.log(err); + }); + }); + return pro; + } + + createCloudLinks() { + let _thiss = this; + let params={ + "link-name": this.linkName, + "in-maint": "", + "link-type": "", + "speed-value": "", + "relationship-list": { + "relationship" : [ + { + "related-to": this.selecteTpName1, + "related-link": "/aai/v13/network/pnfs/pnf/"+this.selectedNode1+"/p-interfaces/p-interface/"+this.selecteTpName1 + }, + { + "related-to": this.cloudTp, + "related-link": "/aai/v13/network/pnfs/pnf/"+this.cloudNode+"/p-interfaces/p-interface/"+this.cloudTp + } + ] + } + }; + var pro = new Promise(function (resolve, reject) { + //做一些异步操作 + _thiss.myhttp.createCloudLink(params) + .subscribe((data) => { + resolve(data["status"]); + }, (err) => { + // console.log(err); + }); + }); + return pro; + } + + //本地云TP端口 删除连线 调用接口deleteLink + delLink(): void { + let deltp1 = this.delTp1, + deltp2 = this.delTp2, + version = this.delVersion, + delLinkIndex = this.delLinkIndex; + let params = { + 'logical-link': this.delLinkname, + 'resource-version': version, + }; + this.myhttp.deleteLink(params) + .subscribe((data) => { + if (data["status"] == 'SUCCESS') { + this.delLine(deltp1, deltp2); + console.log(delLinkIndex) + delLinkIndex.remove(); + } + }, (err) => { + console.log(err); + }); + } + + delLine(val1, val2) { + for (let i = 0; i < $(".node").length; i++) { + if ($('.node').eq(i).find('text').html() == val1) { + $('.node').eq(i).hide(); + } + if ($('.node').eq(i).find('text').html() == val2) { + $('.node').eq(i).hide(); + } + } + this.delBoxisVisible = false; + } + + //外部云 删除连线 调用接口deleteCloudLine + delCloudLink() : void { + let deltp1 = this.delTp1, + deltp2 = this.delTp2, + version = this.delVersion; + let params = { + 'logical-link': this.delLinkname, + 'resource-version': version, + }; + this.myhttp.deleteLink(params) + .subscribe((data) => { + console.log(data) + if (data["status"] == 'SUCCESS') { + this.delLine(deltp1, deltp2); + $('.cloudline').remove(); + } + }, (err) => { + console.log(err); + }); + } + +} diff --git a/usecaseui-portal/src/app/components/charts/bar/bar.component.html b/usecaseui-portal/src/app/components/charts/bar/bar.component.html new file mode 100644 index 00000000..a7cd0677 --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/bar/bar.component.html @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<p> + bar works! +</p> diff --git a/usecaseui-portal/src/app/components/charts/bar/bar.component.less b/usecaseui-portal/src/app/components/charts/bar/bar.component.less new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/bar/bar.component.less diff --git a/usecaseui-portal/src/app/components/charts/bar/bar.component.spec.ts b/usecaseui-portal/src/app/components/charts/bar/bar.component.spec.ts new file mode 100644 index 00000000..d979ffb6 --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/bar/bar.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BarComponent } from './bar.component'; + +describe('BarComponent', () => { + let component: BarComponent; + let fixture: ComponentFixture<BarComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BarComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/components/charts/bar/bar.component.ts b/usecaseui-portal/src/app/components/charts/bar/bar.component.ts new file mode 100644 index 00000000..fa9ecbbd --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/bar/bar.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-bar', + templateUrl: './bar.component.html', + styleUrls: ['./bar.component.less'] +}) +export class BarComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/usecaseui-portal/src/app/components/charts/line/line.component.html b/usecaseui-portal/src/app/components/charts/line/line.component.html new file mode 100644 index 00000000..9a43e28c --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/line/line.component.html @@ -0,0 +1,22 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<div echarts +[initOpts]="initOpts" +[options]="lineOption" +[merge]="updateOption" +(chartInit)="chartInit($event)"> + Line Chart +</div> diff --git a/usecaseui-portal/src/app/components/charts/line/line.component.less b/usecaseui-portal/src/app/components/charts/line/line.component.less new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/line/line.component.less diff --git a/usecaseui-portal/src/app/components/charts/line/line.component.spec.ts b/usecaseui-portal/src/app/components/charts/line/line.component.spec.ts new file mode 100644 index 00000000..afe70654 --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/line/line.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LineComponent } from './line.component'; + +describe('LineComponent', () => { + let component: LineComponent; + let fixture: ComponentFixture<LineComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LineComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LineComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/components/charts/line/line.component.ts b/usecaseui-portal/src/app/components/charts/line/line.component.ts new file mode 100644 index 00000000..8bc8ebd8 --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/line/line.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { SimpleChanges } from '@angular/core/src/metadata/lifecycle_hooks'; + +@Component({ + selector: 'app-line', + templateUrl: './line.component.html', + styleUrls: ['./line.component.less'] +}) +export class LineComponent implements OnInit { + // 图形数据 + @Input() chartData; + // 初始化数据 + @Input() initData; + + constructor() { } + + ngOnInit() { + this.initOpts = { + renderer: 'canvas', + height: this.initData.height, + width: this.initData.width + }; + this.lineOption ={ + tooltip : this.initData.option.tooltip, + icon:'circle', + legend: this.initData.option.legend, + grid: { + left: '1%', + right: '3%', + top: '10%', + bottom: '10%', + containLabel: true + }, + xAxis: { + axisTick: { + show: false, + }, + axisLine:{ + show: false + }, + data: ['01','02','04','06','08','10','12','14','16','18','20','22','24'] + }, + yAxis: { + axisTick: { + show: false, + }, + axisLine:{ + show: false + } + }, + series : this.initData.option.series + } + } + + ngOnChanges(changes:SimpleChanges){ + + // 当有实例的时候再执行,相当于第一次不执行下面方法 + if(this.chartIntance){ + this.chartDataChange() + } + } + // 初始化图形高度 + initOpts:any; + // 折线图配置 + lineOption:any; + // 实例对象 + chartIntance:any; + // 数据变化 + updateOption:any; + chartDataChange(){ + this.updateOption = this.chartData; + } + chartInit(chart){ + this.chartIntance = chart; + } + + + +} diff --git a/usecaseui-portal/src/app/components/charts/pie/pie.component.html b/usecaseui-portal/src/app/components/charts/pie/pie.component.html new file mode 100644 index 00000000..5f1e94ce --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/pie/pie.component.html @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<div echarts +[initOpts]="initOpts" +[options]="pieOption" +[merge]="updateOption" +(chartInit)="chartInit($event)" +(chartMouseOver)="pieMouseOver($event)" +(chartMouseOut)="pieMouseOut($event)"> + Pie Chart +</div>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/components/charts/pie/pie.component.less b/usecaseui-portal/src/app/components/charts/pie/pie.component.less new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/pie/pie.component.less diff --git a/usecaseui-portal/src/app/components/charts/pie/pie.component.spec.ts b/usecaseui-portal/src/app/components/charts/pie/pie.component.spec.ts new file mode 100644 index 00000000..528da25c --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/pie/pie.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PieComponent } from './pie.component'; + +describe('PieComponent', () => { + let component: PieComponent; + let fixture: ComponentFixture<PieComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PieComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PieComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/components/charts/pie/pie.component.ts b/usecaseui-portal/src/app/components/charts/pie/pie.component.ts new file mode 100644 index 00000000..8a5e2100 --- /dev/null +++ b/usecaseui-portal/src/app/components/charts/pie/pie.component.ts @@ -0,0 +1,104 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { SimpleChanges } from '@angular/core/src/metadata/lifecycle_hooks'; + +@Component({ + selector: 'app-pie', + templateUrl: './pie.component.html', + styleUrls: ['./pie.component.less'] +}) +export class PieComponent implements OnInit { + // 图形数据 + @Input() chartData; + // 初始化数据 + @Input() initData; + + constructor() { } + + ngOnInit() { + this.initOpts = { + renderer: 'canvas', + height: this.initData.height + }; + this.pieOption = { + legend: this.initData.option.legend, + color:this.initData.option.color, + series : [ + { + name: this.initData.option.series[0].name, + type: 'pie', + radius : this.initData.option.series[0].radius, + center: ['50%', '45%'], + legendHoverLink: false, + hoverOffset: 5, + avoidLabelOverlap: false, + label: this.initData.option.series[0].label, + data:[ + {value:1, name:'11'} + ], + itemStyle: { + emphasis: { + shadowBlur: 5, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + } + } + + ngOnChanges(changes:SimpleChanges){ + + // 当有实例的时候再执行,相当于第一次不执行下面方法 + if(this.chartIntance){ + this.chartDataChange() + } + } + + // 初始化图形高度 + initOpts:any; + // alarm饼图 + pieOption:any; + // 实例对象 + chartIntance:any; + // 数据变化 + updateOption:any; + chartDataChange(){ + this.updateOption = this.chartData; + // 要等到updateOption渲染完再执行 + this.chartIntance.on('finished',()=>{ + this.chartIntance.dispatchAction({ + type:'highlight', + seriesIndex: 0, + dataIndex:0 + }) + // 由于所有视图变化渲染都会执行,更新完注销此事件 + this.chartIntance.off('finished') + }) + } + + chartInit(chart){ + this.chartIntance = chart; + } + + pieMouseOver(e){ + this.chartIntance.dispatchAction({ + type:'downplay' + }) + this.chartIntance.dispatchAction({ + type:'highlight', + seriesIndex: 0, + dataIndex:e.dataIndex + }) + } + + pieMouseOut(e){ + this.chartIntance.dispatchAction({ + type:'highlight', + seriesIndex: 0, + dataIndex:e.dataIndex + }) + } + + +} diff --git a/usecaseui-portal/src/app/components/details/details.component.css b/usecaseui-portal/src/app/components/details/details.component.css new file mode 100644 index 00000000..0d450d28 --- /dev/null +++ b/usecaseui-portal/src/app/components/details/details.component.css @@ -0,0 +1,104 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.content .header { + background-color: #fff; + border-radius: 5px; + padding: 30px 28px 0; + position: relative; + margin-bottom: 30px; + border: 1px solid #e4e4e4; +} +.content .header hr { + margin: 0; + border: none; + height: 1px; + background-color: #e4e4e4; +} +.content .header h2 { + font: 700 24px/18px 'Times New Roman'; + color: #3fa8eb; + text-align: center; + margin-bottom: 27px; +} +.content .header span.tildeimg { + position: absolute; + left: 50%; + top: 71px; + width: 60px; + height: 10px; + transform: translate(-30px, 0); + background: url(../../../assets/images/tildeimg.png) no-repeat center center; + background-color: #fff; +} +.content .header .headerlist { + display: flex; +} +.content .header .headerlist div { + width: 100%; +} +.content .header .headerlist div p { + font: 400 14px 'Arial'; + color: #323437; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 18px 0 15px; +} +.content .header .headerlist div p span { + display: inline-block; + width: 50%; + text-align: right; + font: 700 14px 'Arial'; + color: #3fa8eb; +} +.content hr { + margin: 0; + border: none; + height: 1px; + background-color: #e4e4e4; +} +.content button { + width: 88px; + height: 22px; + position: absolute; + left: 50%; + transform: translate(-44px, 0); + border: none; + outline: none; + cursor: pointer; + background-color: #fff; + background: url(../../../assets/images/open-close2.png) no-repeat center -22px; +} +.content button:hover { + background: url(../../../assets/images/open-close2.png) no-repeat center -66px; +} +.content .buttonActive { + transform: translate(-44px, -22px); + background: url(../../../assets/images/open-close2.png) no-repeat center 0px; +} +.content .buttonActive:hover { + background: url(../../../assets/images/open-close2.png) no-repeat center -44px; +} +.content h2.detailtitle { + font: 700 24px/18px 'Times New Roman'; + color: #3fa8eb; + text-align: center; + margin-bottom: 15px; + padding-top: 25px; +} +.content .detailInformatioin { + overflow: hidden; +} diff --git a/usecaseui-portal/src/app/components/details/details.component.html b/usecaseui-portal/src/app/components/details/details.component.html new file mode 100644 index 00000000..b5c576c7 --- /dev/null +++ b/usecaseui-portal/src/app/components/details/details.component.html @@ -0,0 +1,68 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<div class="content"> + <div class="header"> + <h2>HEADER INFORMATION</h2> + <hr> <span class="tildeimg"></span> + <div class="headerlist"> + <div class="leftlist"> + <p> <span>Version :</span> </p> + <p> <span>Domain :</span> </p> + <p> <span>Event Name :</span> Fault_IMSSBC_Backup MPU is down </p> + <p> <span>Event Id :</span> 15105642034 </p> + <p> <span>EventType :</span> </p> + <p> <span>NfcNamingCode :</span> </p> + <p> <span>NfNamingCode :</span> </p> + <p> <span>SourceName :</span> </p> + <p> <span>SourceId :</span> </p> + <p> <span>ReportingEntityName :</span> </p> + <p> <span>ReportingEntityId :</span> </p> + </div> + <div class="rightlist"> + <p> <span>Priority :</span> Normal </p> + <p> <span>ReportTime :</span> </p> + <p> <span>ClearTime :</span> </p> + <p> <span>FaultFieldsVersion :</span> </p> + <p> <span>Event Servrity :</span> </p> + <p> <span>EventSourceType :</span> </p> + <p> <span>EventCategory :</span> </p> + <p> <span>AlarmCondition :</span> </p> + <p> <span>SpecificProblem :</span> </p> + <p> <span>Status :</span> </p> + <p> <span>AlarmInterfaceA :</span> </p> + </div> + </div> + </div> + <hr> + <button [ngClass]="{'buttonActive':moredetailShow}" (click)="slideUpDown()"></button> + <h2 class="detailtitle">DETAIL INFORMATION</h2> + <div class="detailInformatioin" [@slideUpDown]='state'> + <nz-table #detailTable [nzData]="detailData" [nzShowPagination]="false" nzSize="small" [nzBordered]="true"> + <thead> + <tr> + <th nzWidth="18%">Item Name</th> + <th >Item Value</th> + </tr> + </thead> + <tbody> + <tr *ngFor="let data of detailTable.data"> + <td>{{data.name}}</td> + <td>{{data.value}}</td> + </tr> + </tbody> + </nz-table> + </div> +</div> diff --git a/usecaseui-portal/src/app/components/details/details.component.less b/usecaseui-portal/src/app/components/details/details.component.less new file mode 100644 index 00000000..ad81d8a6 --- /dev/null +++ b/usecaseui-portal/src/app/components/details/details.component.less @@ -0,0 +1,96 @@ + +.content { + .header { + background-color: #fff; + border-radius: 5px; + padding: 30px 28px 0; + position: relative; + margin-bottom: 30px; + border: 1px solid #e4e4e4; + hr { + margin: 0; + border: none; + height: 1px; + background-color: #e4e4e4; + } + h2 { + font: 700 24px/18px 'Times New Roman'; + color: #3fa8eb; + text-align: center; + margin-bottom: 27px; + } + span.tildeimg { + position: absolute; + left: 50%; + top: 71px; + width: 60px; + height: 10px; + transform: translate(-30px,0); + background: url(../../../assets/images/tildeimg.png) no-repeat center center; + background-color: #fff; + } + .headerlist { + display: flex; + div { + width: 100%; + p { + font: 400 14px 'Arial'; + color: #323437; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 18px 0 15px; + span { + display: inline-block; + width: 50%; + text-align: right; + font: 700 14px 'Arial'; + color: #3fa8eb; + } + } + } + } + } + hr { + margin: 0; + border: none; + height: 1px; + background-color: #e4e4e4; + } + button { + width: 88px; + height: 22px; + position: absolute; + left: 50%; + transform: translate(-44px,0); + border: none; + outline: none; + cursor: pointer; + background-color: #fff; + background: url(../../../assets/images/open-close2.png) no-repeat center -22px; + &:hover { + background: url(../../../assets/images/open-close2.png) no-repeat center -66px; + } + } + .buttonActive { + transform: translate(-44px,-22px); + background: url(../../../assets/images/open-close2.png) no-repeat center -0px; + &:hover { + background: url(../../../assets/images/open-close2.png) no-repeat center -44px; + } + } + h2.detailtitle { + font: 700 24px/18px 'Times New Roman'; + color: #3fa8eb; + text-align: center; + margin-bottom: 15px; + padding-top: 25px; + } + .detailInformatioin { + // transition: all 0.3s linear; + overflow: hidden; + } + .detailshow { + + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/components/details/details.component.spec.ts b/usecaseui-portal/src/app/components/details/details.component.spec.ts new file mode 100644 index 00000000..1d5cbeb8 --- /dev/null +++ b/usecaseui-portal/src/app/components/details/details.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DetailsComponent } from './details.component'; + +describe('DetailsComponent', () => { + let component: DetailsComponent; + let fixture: ComponentFixture<DetailsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DetailsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/components/details/details.component.ts b/usecaseui-portal/src/app/components/details/details.component.ts new file mode 100644 index 00000000..f9e6474b --- /dev/null +++ b/usecaseui-portal/src/app/components/details/details.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { slideUpDown } from '../../animates'; + +@Component({ + selector: 'app-details', + templateUrl: './details.component.html', + styleUrls: ['./details.component.less'], + animations: [ slideUpDown ] +}) +export class DetailsComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + + ngOnChanges(changes){ + console.log(changes); + } + // 详情显示 + moredetailShow = false; + @Input() detailId; + detailData = [ + {name:"DNS.AttDnsQuery",value:"0"}, + {name:"DNS.SuccDnsQuery",value:"0"}, + {name:"DNS.SuccDnsQuery",value:"0"}, + {name:"DNS.SuccDnsQuery",value:"0"}, + {name:"DNS.SuccDnsQuery",value:"0"}, + {name:"sssssss",value:"1111"}, + ] + state = 'up' + slideUpDown(){ + this.moredetailShow = !this.moredetailShow; + this.state = this.state === 'up' ? 'down' : 'up'; + } +} diff --git a/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.css b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.css new file mode 100644 index 00000000..7268b5dc --- /dev/null +++ b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.css @@ -0,0 +1,93 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.content .title { + border-radius: 5px 5px 0 0; + background-color: #fff; + height: 106px; + border-bottom: 1px solid #f0f0f0; + margin-bottom: 0; +} +.content .title ul { + display: flex; + display: -webkit-flex; + justify-content: space-around; + align-items: center; + padding: 0; + margin: 0; + height: 100%; +} +.content .title ul li { + list-style: none; + padding-left: 32px; + width: 100%; + border-left: 1px solid #eceff4; +} +.content .title ul li h5 { + font: 500 14px "Arial"; + color: #3d4d65; +} +.content .title ul li p { + font: 500 24px "Arial"; + color: #3fa8eb; + margin-bottom: 0; +} +.content .title ul li:nth-child(1) { + border: none; +} +.content .chart { + background-color: #fff; + position: relative; + border-radius: 5px; + margin-bottom: 20px; + height: 106px; +} +.content .chart .select { + padding: 20px 50px; + width: 70%; + float: left; +} +.content .chart .select nz-dropdown { + margin-right: 20px; +} +.content .chart .select nz-dropdown a { + font: 700 12px "Arial"; + color: #3d4d65; +} +.content .chart .select nz-dropdown a:hover { + color: #3fa8eb; +} +.content .chart .AlarmChart { + width: 25%; + padding-top: 7px; + float: left; +} +.content .tablelist { + background-color: #fff; + padding: 24px 10px 0px; + border-radius: 0 0 5px 5px; +} +.content .tablelist .action { + padding: 10px 0 0 20px; +} +.content .tablelist .action .details { + display: inline-block; + width: 16px; + height: 16px; + background: url(../../../assets/images/icon.png) center -113px; +} +.content .tablelist .action .details:hover { + background: url(../../../assets/images/icon.png) no-repeat center -128px; +} diff --git a/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.html b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.html new file mode 100644 index 00000000..19c7d853 --- /dev/null +++ b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.html @@ -0,0 +1,79 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<div class="content"> + <!-- <div class="title"> + <ul> + <li><h5>CPU</h5> <p>{{alarmList.all }}</p></li> + <li><h5>Memory</h5> <p>{{alarmList.closed }}</p></li> + <li><h5>Disk</h5> <p>{{alarmList.alarm }}</p></li> + </ul> + </div> --> + <nz-modal nzWrapClassName="vertical-center-modal" [(nzVisible)]="isVisibleMiddle" nzTitle="Alarm Chart" (nzOnCancel)="handleCancelMiddle()" (nzOnOk)="handleOkMiddle()"> + <app-line [initData]="alarmChartInitBig" [chartData]="alarmChartDataBig"></app-line> + </nz-modal> + <div class="chart"> + <div class="select"> + <nz-dropdown [nzTrigger]="'click'"> + <a nz-dropdown> + {{MeasurementSelected}} <i class="anticon anticon-down"></i> + </a> + <ul nz-menu> + <li nz-menu-item (click)="choseMeasurement(item)" *ngFor="let item of MeasurementList">{{item}}</li> + </ul> + </nz-dropdown> + <nz-dropdown [nzTrigger]="'click'"> + <a nz-dropdown> + {{ReportTimeSelected}} <i class="anticon anticon-down"></i> + </a> + <ul nz-menu> + <li nz-menu-item (click)="choseReportTime(item)" *ngFor="let item of ReportTimeList">{{item}}</li> + </ul> + </nz-dropdown> + </div> + <div class="AlarmChart" (click)="showModalMiddle()"> + <app-line [initData]="alarmChartInit" [chartData]="alarmChartData"></app-line> + </div> + <!-- <button class="open-close" [ngClass]="{'open-close-active':alarmShow}" (click)="alarmShow=!alarmShow"></button> --> + </div> + <div class="tablelist"> + <nz-table #nzTable [nzData]="dataSet" [nzPageSize]="10" nzShowSizeChanger [nzPageSizeOptions]="[5,10,15,20]" nzSize="middle"> + <thead (nzSortChange)="sort($event)" nzSingleSort> + <tr> + <th nzWidth="5%">NO</th> + <th nzWidth="20%">Source Name</th> + <th nzWidth="10%">Priority</th> + <th nzWidth="20%">SpecificProblem</th> + <th nzWidth="20%">Report Time</th> + <th nzWidth="15%">Status</th> + <th nzWidth="10%">Action</th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.age}}</td> + <td>{{item.address}}</td> + <td>{{item.address}}</td> + <td>{{item.address}}</td> + <td><a class="action" (click)="detailShow(i+1)"><i class="details"></i></a></td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> + </div> +</div> diff --git a/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.less b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.less new file mode 100644 index 00000000..30fea328 --- /dev/null +++ b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.less @@ -0,0 +1,108 @@ +.content { + .title { + border-radius: 5px 5px 0 0; + background-color: #fff; + height: 106px; + border-bottom: 1px solid #f0f0f0; + margin-bottom: 0; + ul { + display: flex; + display: -webkit-flex; + justify-content: space-around; + align-items: center; + padding: 0; + margin: 0; + height: 100%; + li { + list-style: none; + padding-left: 32px; + width: 100%; + border-left: 1px solid #eceff4; + h5 { + font: 500 14px "Arial"; + color: #3d4d65; + } + p { + font: 500 24px "Arial"; + color: #3fa8eb; + margin-bottom: 0; + } + } + li:nth-child(1){ + border: none; + } + } + } + .chart { + background-color: #fff; + position: relative; + border-radius: 5px; + margin-bottom: 20px; + height: 106px; + .select { + padding: 20px 50px; + width: 70%; + float: left; + nz-dropdown { + margin-right: 20px; + a { + font: 700 12px "Arial"; + color: #3d4d65; + &:hover { + color: #3fa8eb; + } + } + } + } + .AlarmChart { + // height: 0px; + // border-bottom: 1px solid #f5f5f5; + // transition: all 0.3s linear; + width: 25%; + padding-top: 7px; + float: left; + } + // .alarmChart-active { + // height: 386px; + // } + // .open-close { + // width: 50px; + // height: 25px; + // position: absolute; + // left: 50%; + // bottom: 0px; + // transform: translate(-25px,0); + // border: none; + // outline: none; + // cursor: pointer; + // background-color: #fff; + // background: url(../../../assets/images/open-close.png) no-repeat center -27px; + // &:hover { + // background: url(../../../assets/images/open-close.png) no-repeat center -79px; + // } + // } + // .open-close-active { + // background: url(../../../assets/images/open-close.png) center -1px; + // &:hover { + // background: url(../../../assets/images/open-close.png) no-repeat center -53px; + // } + // } + } + .tablelist { + background-color: #fff; + padding: 24px 10px 0px; + border-radius: 0 0 5px 5px; + .action{ + padding: 10px 0 0 20px; + .details{ + display: inline-block; + width: 16px; + height: 16px; + background: url(../../../assets/images/icon.png) center -113px; + &:hover { + background: url(../../../assets/images/icon.png) no-repeat center -128px; + } + } + } + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.spec.ts b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.spec.ts new file mode 100644 index 00000000..013db38a --- /dev/null +++ b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GraphiclistComponent } from './graphiclist.component'; + +describe('GraphiclistComponent', () => { + let component: GraphiclistComponent; + let fixture: ComponentFixture<GraphiclistComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ GraphiclistComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GraphiclistComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.ts b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.ts new file mode 100644 index 00000000..fc86ee0d --- /dev/null +++ b/usecaseui-portal/src/app/components/graphiclist/graphiclist.component.ts @@ -0,0 +1,213 @@ +import { Component, OnInit, Input, Output, EventEmitter, HostBinding } from '@angular/core'; + +@Component({ + selector: 'app-graphiclist', + templateUrl: './graphiclist.component.html', + styleUrls: ['./graphiclist.component.less'] +}) +export class GraphiclistComponent implements OnInit { + + constructor() { } + isVisibleMiddle = false; + + showModalMiddle(): void { + this.isVisibleMiddle = true; + } + handleOkMiddle(): void { + console.log('click ok'); + this.isVisibleMiddle = false; + } + handleCancelMiddle(): void { + this.isVisibleMiddle = false; + } + + ngOnInit() { + } + + // 筛选框(下拉框) + MeasurementList = ['aaaa','bbbb','cccc','dddddDDDD']; + MeasurementSelected = "Measurement"; + ReportTimeList = ['aaaa','bbbb','cccc','ddddd']; + ReportTimeSelected = "ReportTime"; + choseMeasurement(item){ + console.log(item); + this.MeasurementSelected = item; + } + choseReportTime(item){ + console.log(item); + this.ReportTimeSelected = item; + } + + sort(e){ + + } + // 数量统计 + alarmList = { + all:22439, + closed:37923, + alarm: 12342 + } + + //折线图 + alarmChartData:Object; + alarmChartInit:Object = { + height:100, + width:290, + option:{ + tooltip : { + show : false, + }, + legend: { + show :false, + }, + series: [ + { + name: 'Memory', + type: 'bar', + legendHoverLink: true, + barWidth: "25%", + //timeframe_one + data: [40, 45, 38, 52, 64, 58, 69, 87, 76, 33, 64, 87, 45], + itemStyle: { + color: "#3fa8eb" + }, + } + ] + } + }; + //折线图 + alarmChartDataBig:Object; + alarmChartInitBig:Object = { + height:240, + width:500, + option:{ + tooltip : { + show : true, + trigger: 'axis', + }, + legend: { + bottom: 'bottom', + data: ['Memory'] + }, + series: [ + { + name: 'Memory', + type: 'bar', + legendHoverLink: true, + barWidth: "25%", + //timeframe_one + data: [40, 45, 38, 52, 64, 58, 69, 87, 76, 33, 64, 87, 45], + itemStyle: { + color: "#3fa8eb" + }, + } + ] + } + }; + + //表格数据 + dataSet = [ + { + name : 'John Brown', + age : 32, + expand : false, + address : 'New York No. 1', + description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.' + }, + { + name : 'Aim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Xim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'aoe Black', + age : 32, + expand : false, + address : 'Sidney No. 1', + description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.' + } + ]; + + @Output() detailData = new EventEmitter(); + detailShow(id){ + let prems = { + id:id, + detailShow: true + } + this.detailData.emit(prems); + + } + +} diff --git a/usecaseui-portal/src/app/dataInterface.ts b/usecaseui-portal/src/app/dataInterface.ts new file mode 100644 index 00000000..fe42594e --- /dev/null +++ b/usecaseui-portal/src/app/dataInterface.ts @@ -0,0 +1,38 @@ + +interface homeData { + services:{ + number:number, + chartdata:Object[] + }, + performance:{ + per_Vnf:number, + per_VmPm:number + }, + alarm:{ + chartdata:Object[] + }, + Vm_performance:{ + names:string[] + } +}; + +interface homeVmLineData { + CPU:number[], + Memory:number[] +} + +interface servicesSelectData { + customer:string[], + serviceType:string[] +} + +interface servicesTableData { + total:number, + tableList:string[] +} + +interface onboardTableData { + total:number, + tableList:string[] +} +export {homeData, homeVmLineData, servicesSelectData, servicesTableData, onboardTableData} diff --git a/usecaseui-portal/src/app/home/home.component.css b/usecaseui-portal/src/app/home/home.component.css new file mode 100644 index 00000000..d2e51e40 --- /dev/null +++ b/usecaseui-portal/src/app/home/home.component.css @@ -0,0 +1,157 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.content .services { + float: left; + background-color: #fff; + width: 30%; + height: 628px; + border-radius: 5px; + padding: 28px 22px; +} +.content .services h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; + margin-bottom: 58px; +} +.content .services h3 { + font: 400 48px/48px "Arial"; + color: #3fa8eb; + text-align: center; +} +.content .services h3 span { + font-size: 14px; +} +.content .services p { + font: 400 14px/14px "Arial"; + color: #54657e; + text-align: center; + margin-bottom: 48px; +} +.content .services .tip { + background-color: #eceff4; + color: #3d4d65; + font-size: 16px; + margin: 0 20px; + height: 35px; + line-height: 35px; + border-radius: 5px; +} +.content .rightcontent { + float: left; + padding-left: 15px; + width: 70%; +} +.content .rightcontent .rt-content { + height: 220px; + margin-bottom: 18px; +} +.content .rightcontent .rt-content .poerformance { + float: left; + background-color: #fff; + height: 100%; + width: 50%; + border-radius: 5px; + padding: 28px 26px; +} +.content .rightcontent .rt-content .poerformance h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; + margin-bottom: 34px; +} +.content .rightcontent .rt-content .poerformance div { + height: 57px; + position: relative; + margin-bottom: 10px; +} +.content .rightcontent .rt-content .poerformance div h3 { + font: 600 25px/25px "Arial"; + color: #3d4d65; + margin-bottom: 10px; +} +.content .rightcontent .rt-content .poerformance div p { + font: 400 12px/12px "Arial"; + color: #54657e; +} +.content .rightcontent .rt-content .poerformance div img { + position: absolute; + top: 0; + right: 0; +} +.content .rightcontent .rt-content .alarm { + float: left; + background-color: #fff; + height: 100%; + width: 48%; + margin-left: 2%; + border-radius: 5px; + position: relative; + padding: 28px 26px; +} +.content .rightcontent .rt-content .alarm h4 { + position: absolute; + font: 600 16px/16px "Arial"; + color: #3d4d65; +} +.content .rightcontent .rb-content { + height: 390px; + background-color: #fff; + border-radius: 5px; + padding: 24px 30px; + position: relative; +} +.content .rightcontent .rb-content h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; +} +.content .rightcontent .rb-content nz-dropdown { + position: absolute; + top: 24px; + right: 30px; +} +.content .rightcontent .rb-content nz-dropdown button { + width: 170px; + height: 35px; + background-color: #eceff4; + text-align: left; + border-color: #cad3df; +} +.content .rightcontent .rb-content nz-dropdown button span { + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px; +} +.content .rightcontent .rb-content nz-dropdown button i { + position: absolute; + top: 12px; + right: 12px; +} +.content .rightcontent .rb-content #pfVmChartLine { + width: 100%; + height: 318px; +} diff --git a/usecaseui-portal/src/app/home/home.component.html b/usecaseui-portal/src/app/home/home.component.html new file mode 100644 index 00000000..b2fa5eb4 --- /dev/null +++ b/usecaseui-portal/src/app/home/home.component.html @@ -0,0 +1,63 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> {{"Overall trend" | translate}}</h3> +<hr> +<div class="content"> + <div class="services"> + <h4>{{"SERVICES" | translate}}</h4> + <h3>{{serviceNumber}} <span>{{"services" | translate}}</span> </h3> + <p>{{serviceNumber}} {{"services has been created" | translate}}</p> + <app-pie [initData]="serviceChartInit" [chartData]="serviceChartData"></app-pie> + <p class="tip">View the details</p> + </div> + + <div class="rightcontent"> + <div class="rt-content"> + <div class="poerformance"> + <h4>{{"PERFORMANCE" | translate}}</h4> + <div class="pfVnf"> + <h3 class="pfVnfNum">126</h3> + <p>Performance VNF</p> + <img src="../../assets/images/VNF.png" alt="VNF"> + </div> + <div class="pfVmPm"> + <h3 class="pfVmPmNum">286</h3> + <p>Performance VM/PM</p> + <img src="../../assets/images/VM.png" alt="VM/PM"> + </div> + </div> + <div class="alarm"> + <h4>{{"Alarm" | translate}}</h4> + <app-pie [initData]="alarmChartInit" [chartData]="alarmChartData"></app-pie> + </div> + </div> + + <div class="rb-content"> + <h4>{{"VM Performance" | translate}}</h4> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomRight'"> + <button nz-button nz-dropdown><span>{{vmPerformanceNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="vmPerformanceNameSelect(item)" *ngFor="let item of vmPerformanceNames"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + <app-line [initData]="performanceChartInit" [chartData]="performanceChartData"></app-line> + </div> + </div> + +</div> + diff --git a/usecaseui-portal/src/app/home/home.component.less b/usecaseui-portal/src/app/home/home.component.less new file mode 100644 index 00000000..89abafa6 --- /dev/null +++ b/usecaseui-portal/src/app/home/home.component.less @@ -0,0 +1,145 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.content { + .services { + float: left; + background-color: #fff; + width: 30%; + height: 628px; + border-radius: 5px; + padding: 28px 22px; + h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; + margin-bottom: 58px; + } + h3 { + font: 400 48px/48px "Arial"; + color: #3fa8eb; + text-align: center; + span { + font-size: 14px; + } + } + p { + font: 400 14px/14px "Arial"; + color: #54657e; + text-align: center; + margin-bottom: 48px; + } + .tip { + background-color: #eceff4; + color: #3d4d65; + font-size: 16px; + margin: 0 20px; + height: 35px; + line-height: 35px; + border-radius: 5px; + } + } + .rightcontent { + float: left; + padding-left: 15px; + width: 70%; + .rt-content { + height: 220px; + margin-bottom: 18px; + .poerformance { + float: left; + background-color: #fff; + height: 100%; + width: 50%; + border-radius: 5px; + padding: 28px 26px; + h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; + margin-bottom: 34px; + } + div { + height: 57px; + position: relative; + margin-bottom: 10px; + h3 { + font: 600 25px/25px "Arial"; + color: #3d4d65; + margin-bottom: 10px; + } + p { + font: 400 12px/12px "Arial"; + color: #54657e + } + img { + position: absolute; + top: 0; + right: 0; + } + } + } + .alarm { + float: left; + background-color: #fff; + height: 100%; + width: 48%; + margin-left: 2%; + border-radius: 5px; + position: relative; + padding: 28px 26px; + h4 { + position: absolute; + font: 600 16px/16px "Arial"; + color: #3d4d65; + } + } + } + .rb-content { + height: 390px; + background-color: #fff; + border-radius: 5px; + padding: 24px 30px; + position: relative; + h4 { + font: 600 16px/16px "Arial"; + color: #3d4d65; + } + nz-dropdown { + position: absolute; + top: 24px; + right: 30px; + button { + width: 170px; + height: 35px; + background-color: #eceff4; + text-align: left; + border-color: #cad3df; + span { + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px; + } + i { + position: absolute; + top: 12px; + right: 12px; + } + } + //下拉框中的样式在style.less中,下拉框是在body中额外临时生成的 + } + #pfVmChartLine { + width: 100%; + height: 318px; + } + } + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/home/home.component.spec.ts b/usecaseui-portal/src/app/home/home.component.spec.ts new file mode 100644 index 00000000..490e81bd --- /dev/null +++ b/usecaseui-portal/src/app/home/home.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture<HomeComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HomeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/home/home.component.ts b/usecaseui-portal/src/app/home/home.component.ts new file mode 100644 index 00000000..39fab25d --- /dev/null +++ b/usecaseui-portal/src/app/home/home.component.ts @@ -0,0 +1,166 @@ +import { Component, OnInit, Input, Output, EventEmitter, HostBinding } from '@angular/core'; +import { MyhttpService } from '../myhttp.service'; +import { slideToRight } from '../animates'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.less'], + animations: [ slideToRight ] +}) +export class HomeComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + + constructor(private myhttp: MyhttpService) { } + + ngOnInit() { + this.getHomeAllData(); + } + + // alarm饼图 + alarmChartData:Object; + alarmChartInit:Object = { + height: 164, + option:{ + legend: { + orient: 'vertical', + left: '0px', + bottom: '0px', + data: ['Active','Closed'] + }, + color:["#fb6e6e","#526b75"], + series:[{ + name:"告警信息", + radius : '55%', + label:{ + normal:{ + show: false, + }, + emphasis: { + show: true, + formatter:'{b}\n{c},{d}%', + } + } + }] + } + }; + + // services饼图 + serviceNumber:number = 0; + serviceChartData:Object; + serviceChartInit:Object = { + height: 300, + option:{ + legend: { + orient: 'vertical', + left: '0px', + bottom: '0px', + data: ['Active','Closed'] + }, + color:["#3fa8eb","#1abb9b","#a4ead7"], + series:[{ + name:"服务信息", + radius : ['45%','65%'], + avoidLabelOverlap: false, + label:{ + normal:{ + show: false, + position: 'center' + }, + emphasis: { + show: true, + formatter:'{b}\n{c}', + textStyle: { + fontSize: '20', + fontWeight: 'bold' + } + } + }, + labelLine: { + normal: { + show: false + } + }, + }] + } + }; + + // Performance线图 + performanceChartData:Object; + performanceChartInit:Object = { + height:320, + option:{ + legend: { + bottom: '0px', + data: ['CPU','Memory'] + }, + series : [ + { + name: 'CPU', + type: 'line', + data:[20,23,14,12,34,25,22,42,52,35,34,13,13] + }, + { + name: 'Memory', + type: 'line', + data:[10,23,24,22,14,15,32,12,12,32,14,23,23] + } + ] + } + }; + getPerformanceChartData(){ + let paramsObj = { + vmPerformanceName:this.vmPerformanceNameSelected + } + this.myhttp.getHomePerformanceChartData(paramsObj) + .subscribe((data)=>{ + this.performanceChartData = { + series:[ + {data:data.CPU}, + {data:data.Memory} + ] + } + },(err)=>{ + console.log(err); + }) + } + + // vm筛选框 + vmPerformanceNames = ['performanceNameOne']; + vmPerformanceNameSelected = this.vmPerformanceNames[0]; + vmPerformanceNameSelect(item){ + if(this.vmPerformanceNameSelected != item){ + console.log(item); + this.vmPerformanceNameSelected = item; + this.getPerformanceChartData() + } + } + + // 获取数据 + getHomeAllData(){ + this.myhttp.getAllHomeData() + .subscribe( + (data)=>{ + console.log(data); + this.alarmChartData ={ + series:[{ + data:data.alarm.chartdata + }] + }; + this.serviceNumber = data.services.number; + this.serviceChartData ={ + series:[{ + data:data.services.chartdata + }] + }; + this.vmPerformanceNames = data.Vm_performance.names; + this.vmPerformanceNameSelected = this.vmPerformanceNames[0]; + this.getPerformanceChartData(); + }, + (err)=>{ + console.log(err); + } + ) + } + +} diff --git a/usecaseui-portal/src/app/myhttp.service.ts b/usecaseui-portal/src/app/myhttp.service.ts new file mode 100644 index 00000000..09f13a49 --- /dev/null +++ b/usecaseui-portal/src/app/myhttp.service.ts @@ -0,0 +1,266 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs/Observable'; +import { homeData, homeVmLineData, servicesSelectData, servicesTableData, onboardTableData} from './dataInterface'; + + + +@Injectable() +export class MyhttpService { + + constructor(private http: HttpClient) { } + + baseUrl = "./assets/json"; + url={ + allhome:this.baseUrl + "/homeAllData.json", + homeLineData:this.baseUrl + "/homePerformanceChartData.json", + serviceSelectList:this.baseUrl + "/servicesList.json", //customer、serviceType + servicesTableData:this.baseUrl + "/servicesTableData.json", + onboardTableData:this.baseUrl + "/onboardTableData.json", + + customers:this.baseUrl + "/customers.json?", + serviceType:this.baseUrl + "/serviceTypes.json?*_*", + servicesCategory:this.baseUrl + "/configuration_files/servicesCategory.json?", + serviceInstanceList:this.baseUrl + "/instanceTableData4.json?", + serviceTemplates:this.baseUrl + "/serviceTemplates2.json?", + templateCategory:this.baseUrl + "/configuration_files/templateCategory.json?", + templateParameters:this.baseUrl + "/*_*" + "ServiceTemplateParameters.json?", + addressData: this.baseUrl + "/siteAddressData.json?", + createService:this.baseUrl + "/createService.json?", + inputNamesTransform: this.baseUrl + "/configuration_files/inputNamesTranslate.json?", + deleteService: this.baseUrl + "/deleteService.json?", + progress:this.baseUrl + "/progress.json?", + + allottedResource:this.baseUrl + "/allotted-resources2.json?", + pnfDetail:this.baseUrl + "/pnfdetail-domain.json?", + connectivity:this.baseUrl + "/sotn-connectivity2.json?", + vpnBinding:this.baseUrl + "/vpnbinding.json?", + alarmFormData:this.baseUrl + "/alarmFormData.json?" + } + + // baseUrl = 'http://172.19.44.223/api/usecaseui-server/v1'; + // baseUrl = '/api/usecaseui-server/v1'; + // url={ + // allhome:this.baseUrl + "/alarm/getAlarmDataByStatus/0", + // homeLineData:this.baseUrl + "/...........", + // serviceSelectList:this.baseUrl + "/xxxxxxxxxxxxx", + // servicesTableData:this.baseUrl + "/xxxxxxxx.json", + // onboardTableData:this.baseUrl + "/xxxxxxx.json", + + + // customers:this.baseUrl + "/uui-lcm/customers", + // serviceType:this.baseUrl + "/uui-lcm/customers/" + "*_*" + "/service-subscriptions", + // servicesCategory: "./assets/json/configuration_files/servicesCategory.json", + // serviceInstanceList:this.baseUrl + '/uui-sotn/getServiceInstanceList', + // serviceTemplates:this.baseUrl + "/uui-lcm/service-templates", + // templateCategory: "./assets/json/configuration_files/templateCategory.json", + // templateParameters:this.baseUrl + "/uui-lcm/service-templates/" + "*_*" +"?toscaModelPath=", + // addressData: this.baseUrl + "/uui-sotn/getOssInvenory", + // createService:this.baseUrl + "/uui-lcm/services", + // inputNamesTransform: "./assets/json/configuration_files/inputNamesTranslate.json?", + // deleteService: this.baseUrl + "/uui-lcm/services/", + // progress:this.baseUrl + "/uui-lcm/services/" + "*_*" + "/operations/", + + // allottedResource:this.baseUrl + "/uui-sotn/getAllottedResources", + // pnfDetail:this.baseUrl + "/uui-sotn/getPnfInfo/", + // connectivity:this.baseUrl + "/uui-sotn/getConnectivityInfo/", + // vpnBinding:this.baseUrl + "/uui-sotn/getPinterfaceByVpnId/" + // } + + // home页数据 + getAllHomeData() { + return this.http.get<homeData>(this.url.allhome); + } + //home页折线图数据 + getHomePerformanceChartData(paramsObj){ + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<homeVmLineData>(this.url.homeLineData,{params}); + } + + // servicesList数据 + // getServicesSelectData():Observable<HttpResponse<servicesSelectData>>{ + // return this.http.get<servicesSelectData>(this.url.serviceSelectList,{observe:'response'}); + // } + getServicesTableData(paramsObj):Observable<HttpResponse<servicesTableData>>{ + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<servicesTableData>(this.url.servicesTableData,{observe:'response',params}); + } + + // onboard数据 + getOnboardTableData(paramsObj):Observable<HttpResponse<onboardTableData>>{ + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<onboardTableData>(this.url.onboardTableData,{observe:'response',params}); + } + + + + + + testObservable(){ + let myObservable = new Observable((observer)=>{ + observer.next(1); + observer.next((n)=>{ + console.log(3+n); + }) + setTimeout(()=>{ + observer.next(66666) + },100) + observer.next(()=>{ + setTimeout((n)=>{ + console.log("9999---" + n); + },10) + }) + // observer.error(2); + // observer.complete(); + }); + + myObservable.subscribe((e)=>{ + if(typeof e == "function"){ + e(5) + } + console.log(e); + },(err)=>{ + console.log(err); + },()=>{ + console.log(555); + }) + } + + //--------------------------------------------------------------------------------- + + // 获取所有customers + getAllCustomers(){ + return this.http.get<any>(this.url.customers); + // return this.http.jsonp<Object[]>('http://127.0.0.1:5500/customers.json',"callback");// 测试用 :请求数据需要用回调函数包裹 + } + + // 获取相应的serviceType + getServiceTypes(customer){ + let url = this.url.serviceType.replace("*_*",customer.id); + return this.http.get<any>(url); + } + // 获取服务分类信息,本地配置文件 + getServicesCategory(){ + return this.http.get<any>(this.url.servicesCategory); + } + // list表格数据 + getInstanceTableData(paramsObj){ + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(this.url.serviceInstanceList,{params}); + } + + // // 服务详情数据 + // getInstanceDetails(id){ + // let url = this.baseUrl + "/detailsData.json?id=" + id; + // return this.http.get<instanceDetail>(url); + // } + + // 获取所有模板类型 + getAllServiceTemplates(){ + return this.http.get<any>(this.url.serviceTemplates); + } + + // 获取模板分类信息,本地配置文件 + getTemplateCategory(){ + return this.http.get<any>(this.url.servicesCategory); + } + // 获取模板输入项参数 + getTemplateParameters(type,template){ + let url = this.url.templateParameters.replace("*_*",type) + template.toscaModelURL; //本地模拟 + // let url = this.url.templateParameters.replace("*_*",template.uuid) + template.toscaModelURL; + return this.http.get<any>(url); + } + // siteAddress 地址 + getSiteAddress(){ + return this.http.get<any>(this.url.addressData); + } + + // 创建接口 + createInstance(requestBody){ + return this.http.get<any>(this.url.createService); //本地模拟 + // return this.http.post<any>(this.url.createService,requestBody); + } + + // 输入参数名字转换 + inputNamesTransform(){ + return this.http.get(this.url.inputNamesTransform); + } + + // 删除接口 + deleteInstance(obj){ + let httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json', + 'Accept':'application/json', + 'Authorization':'Basic SW5mcmFQb3J0YWxDbGllbnQ6cGFzc3dvcmQxJA==' + }), + body:{ + 'globalSubscriberId': obj.globalSubscriberId, + 'serviceType': obj.serviceType + } + }; + return this.http.get<any>(this.url.deleteService); //本地模拟 + // return this.http.delete<any>(this.url.deleteService + obj.serviceInstanceId, httpOptions); + } + + // 查询进度接口 + getProgress(obj){ + let url = this.url.progress.replace("*_*",obj.serviceId) + obj.operationId; + return this.http.get<any>(url); + } + + // 获取allotted-resource 能获取tp和pnf值 + getAllottedResource(obj){ + let params = new HttpParams({fromObject:obj}); + let url = this.url.allottedResource; + return this.http.get<any>(url,{params}); + } + //通过pnf值获取对应的domain (network-resource) + getPnfDetail(name){ + let url = this.url.pnfDetail + name; + return this.http.get<any>(url); + } + //通过sotn 获取connectivity ,查找vpn-id + getSotnConnectivity(id){ + let url = this.url.connectivity + id; + return this.http.get<any>(url); + } + //通过vpn-id 查找tp和pnf + getVpnBinding(id){ + let url = this.url.vpnBinding + id; + return this.http.get<any>(url); + } + // 时间格式化 毫秒转正常值 + dateformater(vmstime){ + if(!vmstime){ + return '' + } + let mstime = Number((vmstime + '').slice(0,13)); + let time = new Date(mstime); + let year = time.getFullYear(); + let month = time.getMonth() + 1; + let day = time.getDate(); + let hours = time.getHours(); + let minutes = time.getMinutes(); + let seconds = time.getSeconds(); + let formattime = year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; + return formattime; + } + + // alarm表格数据 + // getAlarmFormData(pageNumber:number,pageSaze:number,name?:string,Priority?:string,Status?:string,Report?:string){ + // return this.http.post<any>(this.url.alarmFormData,{ + // pageNumber:pageNumber, + // pageSaze:pageSaze, + // name:name, + // Priority:Priority, + // Status:Status, + // Report:Report + // }); + // } + + + getAlarmFormData(pageNumber:number,pageSaze:number,name?:string,Priority?:string,Status?:string,Report?:string){ + return this.http.get<any>(this.url.alarmFormData+'?pageNumber='+pageNumber+'?pageSaze'+pageSaze+'?name'+name+'?Priority'+Priority+'?Status'+Status+'?Report'+Report); + } +} diff --git a/usecaseui-portal/src/app/networkHttpservice.service.ts b/usecaseui-portal/src/app/networkHttpservice.service.ts new file mode 100644 index 00000000..5e713d7f --- /dev/null +++ b/usecaseui-portal/src/app/networkHttpservice.service.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; + +interface tablelist { + total:number, + list:Object[] +} +interface d3list { + total:number, + list:Object[] +} + +@Injectable() +export class networkHttpservice { + + constructor(private http:HttpClient) { } + + baseUrl = "./assets/json/"; + // list表格数据 + getInstanceTableData(paramsObj){ + let url = this.baseUrl + "instanceTableData.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<tablelist>(url,{params}); + } + //d3数据 + getNetworkD3Data(){ + let url = this.baseUrl + "netWorkD3Data.json"; + // let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url); + } + //初始化连线 logical-links + getLogicalLinksData(){ + let url = this.baseUrl + "LogicalLinksData.json"; + // let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url); + } + //查询指定的node对应的tp数据 + getPInterfacesData1(paramsObj){ + let url = this.baseUrl + "p_interfaces1.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + getPInterfacesData2(paramsObj){ + let url = this.baseUrl + "p_interfaces2.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //创建连线接口 + createLink(paramsObj){ + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //查询指定的单个连接线 接口 + querySpecificLinkInfo(paramsObj){ + let url = this.baseUrl+ "specific_link _nfo.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + + //查询 外部云host this.url地址 接口 + queryCloudUrl(paramsObj){ + let url = this.baseUrl+ "url.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + + //创建外部云newwork接口 + createNetwrok(paramsObj){ + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //创建外部云pnf接口 + createPnf(paramsObj){ + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //创建外部云Tp接口 + createTp(paramsObj,cloudNodeName){ + console.log(cloudNodeName) + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //创建外部云link接口 + createCloudLink(paramsObj){ + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } + //删除连线 + deleteLink(paramsObj){ + let url = this.baseUrl+ "status.json"; + let params = new HttpParams({fromObject:paramsObj}); + return this.http.get<any>(url,{params}); + } +} diff --git a/usecaseui-portal/src/app/networkHttpservice.service1.ts b/usecaseui-portal/src/app/networkHttpservice.service1.ts new file mode 100644 index 00000000..edb4df7c --- /dev/null +++ b/usecaseui-portal/src/app/networkHttpservice.service1.ts @@ -0,0 +1,97 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; + + +@Injectable() +export class networkHttpservice { + + constructor(private http:HttpClient) { } + + // baseUrl = "./assets/json/";//本地环境 + // url={ + // "getNetworkD3Data":this.baseUrl + "netWorkD3Data.json", + // "getLogicalLinksData":this.baseUrl + "LogicalLinksData.json", + // "getPInterfacesData1":this.baseUrl + "netWorkD3Data.json", + // "getPInterfacesData2":this.baseUrl + "p_interfaces1.json", + // "createLink":this.baseUrl + "status.json", + // "querySpecificLinkInfo":this.baseUrl + "specific_link _nfo.json", + // "queryCloudUrl":this.baseUrl + "url.json", + // "createNetwrok":this.baseUrl + "status.json", + // "createPnf":this.baseUrl + "status.json", + // "createTp":this.baseUrl + "status.json", + // "createCloudLink":this.baseUrl + "status.json", + // "createCloudUrl":this.baseUrl + "status.json", + // "deleteLink":this.baseUrl + "status.json", + // }; + // baseUrl = "http://10.73.242.244:8082/uui-sotn/";//线上环境 + // baseUrl = "http://172.19.44.223/api/usecaseui-server/v1/uui-sotn/";//线上环境 + baseUrl = "/api/usecaseui-server/v1/uui-sotn/";//线上环境 + url={ + "getNetworkD3Data":this.baseUrl + "getNetWorkResources", + "getLogicalLinksData":this.baseUrl + "getLogicalLinks", + "getPInterfacesData1":this.baseUrl + "getPinterfaceByPnfName/", + "getPInterfacesData2":this.baseUrl + "getPinterfaceByPnfName/", + "createLink":this.baseUrl + "createLink/", + "querySpecificLinkInfo":this.baseUrl + "getSpecificLogicalLink/", + "queryCloudUrl":this.baseUrl + "getHostUrl/", + "createNetwrok":this.baseUrl + "createTopoNetwork/", + "createPnf":this.baseUrl + "createPnf/", + "createTp":this.baseUrl + "pnf/", + "createCloudLink":this.baseUrl + "createLink/", + "createCloudUrl":this.baseUrl + "createHostUrl/", + "deleteLink":this.baseUrl + "deleteLink/", + }; + //d3数据 + getNetworkD3Data(){ + return this.http.get<any>(this.url["getNetworkD3Data"]); + } + //初始化连线 logical-links + getLogicalLinksData(){ + return this.http.get<any>(this.url["getLogicalLinksData"]); + } + //查询指定的node对应的tp数据 + getPInterfacesData1(paramsObj){ + return this.http.get<any>(this.url['getPInterfacesData1']+paramsObj["pnfName"]); + } + getPInterfacesData2(paramsObj){ + return this.http.get<any>(this.url["getPInterfacesData2"]+paramsObj["pnfName"]); + } + //创建连线接口 + createLink(paramsObj){ + return this.http.put<any>(this.url["createLink"]+paramsObj["link-name"],paramsObj); + } + //查询指定的单个连接线 接口 + querySpecificLinkInfo(paramsObj){ + return this.http.get<any>(this.url["querySpecificLinkInfo"]+paramsObj["link-name"]); + } + //查询 外部云host this.url地址 接口 + queryCloudUrl(aaiId){ + return this.http.get<any>(this.url["queryCloudUrl"]+aaiId); + } + //创建外部云newwork接口 + createNetwrok(paramsObj){ + return this.http.put<any>(this.url["createNetwrok"]+paramsObj["network-id"],paramsObj); + } + //创建外部云pnf接口 + createPnf(paramsObj){ + return this.http.put<any>(this.url["createPnf"]+paramsObj["pnf-name"],paramsObj); + } + //创建外部云Tp接口 + createTp(paramsObj,cloudNodeName){ + let str=cloudNodeName+"/p-interfaces/p-interface/"+paramsObj["interface-name"]+"/createTerminationPoint"; + return this.http.put<any>(this.url["createTp"]+str,paramsObj); + } + //创建外部云link接口 + createCloudLink(paramsObj){ + return this.http.put<any>(this.url["createCloudLink"]+paramsObj["link-name"],paramsObj); + } + //创建外部云host url接口 + createCloudUrl(paramsObj){ + return this.http.put<any>(this.url["createCloudUrl"]+paramsObj["aai-id"],paramsObj); + } + //删除连线 + deleteLink(paramsObj){ + let str=paramsObj["logical-link"]+"/"+paramsObj["resource-version"]; + return this.http.delete<any>((this.url["deleteLink"]+str)); + } +} diff --git a/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.css b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.css new file mode 100644 index 00000000..079a409c --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.css @@ -0,0 +1,75 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select { + margin-bottom: 20px; +} +.select span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; +} +.select nz-dropdown { + vertical-align: middle; +} +.select nz-dropdown :hover { + border-color: #147dc2; +} +.select nz-dropdown button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; +} +.select nz-dropdown button span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; +} +.select nz-dropdown button i { + position: absolute; + top: 10px; + right: 10px; +} +.select .submit { + margin-left: 20px; + vertical-align: middle; + height: 30px; + padding: 0 10px; +} +.select .submit span { + color: #fff; + font-weight: 400; +} +.content { + background-color: #fff; + border-radius: 5px; + padding: 12px; +} diff --git a/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.html b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.html new file mode 100644 index 00000000..76d2485e --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.html @@ -0,0 +1,79 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- <h3 class="title"> + <span (click)="performanceShow()" style="cursor:pointer;">Performance VM</span> + <span (click)="graphicShow()" *ngIf="graphicshow">> Graphic list </span> + <span *ngIf="detailshow">> Details </span> +</h3> +<hr> +<div class="select" [@showHideAnimate]="state"> + <span>Source Name: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{sourceNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseSourceName(item)" *ngFor="let item of sourceNameList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + + <span>ReportingEntityName: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{ReportingEntityNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseReportingEntityName(item)" *ngFor="let item of ReportingEntityNameList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + <button class="submit" nz-button [nzType]="'primary'" (click)="submit()"><i class="anticon anticon-plus-circle-o"></i><span>Submit</span></button> +</div> +<div class="content" [@showHideAnimate]="state"> + <nz-table #nzTable [nzData]="dataSet" [nzPageSize]="10" nzShowSizeChanger nzShowQuickJumper [nzPageSizeOptions]="[5,10,15,20]" nzSize="middle"> + <thead (nzSortChange)="sort($event)" nzSingleSort> + <tr> + <th nzWidth="5%">NO</th> + <th nzWidth="20%">Source Name</th> + <th nzWidth="20%">ReportingEntityName</th> + <th nzWidth="10%">Type</th> + <th nzWidth="15%">CPU</th> + <th nzWidth="10%">Memory</th> + <th nzWidth="10%">Disk</th> + <th nzWidth="10%">Action</th> + </tr> + </thead> + <tbody> --> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <!-- <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.address}}</td> + <td>{{item.age}}</td> + <td>{{item.address}}</td> + <td>{{item.age}}</td> + <td>{{item.age}}</td> + <td><a (click)="graphicShow()">Delete</a></td> + </tr> --> + <!-- </ng-template> --> + <!-- </tbody> + </nz-table> +</div> +<div [@showHideAnimate]="state2"> + <app-graphiclist (detailData)="detailShow($event)"></app-graphiclist> +</div> +<div [@showHideAnimate]="state3"> + <app-details [detailId]="detailId"></app-details> +</div> --> diff --git a/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.less b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.less new file mode 100644 index 00000000..8a9005c5 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.less @@ -0,0 +1,61 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select { + margin-bottom: 20px; + span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; + } + nz-dropdown { + vertical-align: middle; + :hover{ + border-color: #147dc2; + } + button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; + span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; + } + i { + position: absolute; + top: 10px; + right: 10px; + } + } + //下拉框中的样式在style.less中,下拉框是在body中额外临时生成的 + } + .submit { + margin-left: 20px; + vertical-align: middle; + height: 30px; + padding: 0 10px; + span { + color: #fff; + font-weight: 400; + } + } +} +.content { + background-color: #fff; + border-radius: 5px; + padding: 12px; +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.spec.ts b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.spec.ts new file mode 100644 index 00000000..fd441da3 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PerformanceVmComponent } from './performance-vm.component'; + +describe('PerformanceVmComponent', () => { + let component: PerformanceVmComponent; + let fixture: ComponentFixture<PerformanceVmComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PerformanceVmComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PerformanceVmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.ts b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.ts new file mode 100644 index 00000000..1fb099c9 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vm/performance-vm.component.ts @@ -0,0 +1,159 @@ +import { Component, OnInit, HostBinding } from '@angular/core'; +import { slideToRight, showHideAnimate } from '../../animates'; + +@Component({ + selector: 'app-performance-vm', + templateUrl: './performance-vm.component.html', + styleUrls: ['./performance-vm.component.less'], + animations: [ slideToRight, showHideAnimate ] +}) +export class PerformanceVmComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + constructor() { } + + ngOnInit() { + } + + // 筛选框(下拉框) + sourceNameList = ['aaaa','bbbb','cccc','dddddDDDDDDDDDDDDDDD']; + sourceNameSelected = this.sourceNameList[0]; + ReportingEntityNameList = ['aaaa','bbbb','cccc','ddddd']; + ReportingEntityNameSelected = this.ReportingEntityNameList[0]; + choseSourceName(item){ + console.log(item); + this.sourceNameSelected = item; + } + choseReportingEntityName(item){ + console.log(item); + this.ReportingEntityNameSelected = item; + } + + //表格数据 + dataSet = [ + { + name : 'John Brown', + age : 32, + expand : false, + address : 'New York No. 1', + description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.' + }, + { + name : 'Aim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Xim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'Jim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'cim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'bim Green', + age : 42, + expand : false, + address : 'London No. 1', + description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.' + }, + { + name : 'aoe Black', + age : 32, + expand : false, + address : 'Sidney No. 1', + description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.' + } + ]; + + //详情页标题显示 + graphicshow = false; + detailshow = false; + // 显示隐藏动画 + state = "show"; + state2 = "hide"; + state3 = "hide"; + performanceShow() { + this.state = 'show'; + this.state2 = 'hide'; + this.state3 = 'hide'; + this.graphicshow = false; + this.detailshow = false; + } + graphicShow() { + this.state = 'hide'; + this.state2 = 'show'; + this.state3 = 'hide'; + this.graphicshow = true; + this.detailshow = false; + } + // 选中id + detailId:number; + detailShow(prems) { + this.state = 'hide'; + this.state2 = 'hide'; + this.state3 = 'show'; + this.graphicshow = true; + this.detailshow = true; + console.log(prems); + this.detailId = prems.id; + } + +} diff --git a/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.css b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.css new file mode 100644 index 00000000..4ed015fe --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.css @@ -0,0 +1,126 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select { + margin-bottom: 20px; +} +.select span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; +} +.select nz-dropdown { + vertical-align: middle; +} +.select nz-dropdown :hover { + border-color: #147dc2; +} +.select nz-dropdown button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; +} +.select nz-dropdown button span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; +} +.select nz-dropdown button i { + position: absolute; + top: 10px; + right: 10px; +} +.select .submit { + margin-left: 20px; + vertical-align: middle; + height: 30px; + padding: 0 10px; +} +.select .submit span { + color: #fff; + font-weight: 400; +} +.content { + background-color: #fff; + border-radius: 5px; + padding: 12px; +} +.content .vnfs { + display: flex; + flex-wrap: wrap; + justify-content: space-around; +} +.content .vnfs .vnf { + width: 18%; + height: 200px; + margin: 5px; + padding: 20px; + border-radius: 2px; + text-align: center; + cursor: pointer; + transition: all 0.3s linear; +} +.content .vnfs .vnf:hover { + background-color: #f5f5f5; + transform: scale(1.02); +} +.content .vnfs .vnf h3 { + font-size: 14px; + color: #3fa8eb; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; +} +.content .vnfs .vnf .intro { + text-align: left; + font-size: 12px; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + word-wrap: break-word; + word-break: break-all; +} +.content .vnfs .empty { + width: 18%; + height: 200px; + margin: 5px; + border-radius: 2px; +} +.content .pages { + height: 25px; + margin: 20px 10px; + position: relative; +} +.content .pages nz-pagination { + float: right; +} diff --git a/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.html b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.html new file mode 100644 index 00000000..0b398703 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.html @@ -0,0 +1,64 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> + <span (click)="performanceShow()" style="cursor:pointer;">Performance VNF</span> + <span (click)="graphicShow()" *ngIf="graphicshow">> Graphic list </span> + <span *ngIf="detailshow">> Details </span> +</h3> +<hr> +<div class="select" [@showHideAnimate]="state"> + <span>Source Name: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{sourceNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseSourceName(item)" *ngFor="let item of sourceNameList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + + <span>ReportingEntityName: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{ReportingEntityNameSelected}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu> + <li nz-menu-item (click)="choseReportingEntityName(item)" *ngFor="let item of ReportingEntityNameList"> + <a>{{item}}</a> + </li> + </ul> + </nz-dropdown> + <button class="submit" nz-button [nzType]="'primary'" (click)="submit()"><i class="anticon anticon-plus-circle-o"></i><span>Submit</span></button> +</div> +<div class="content" [@showHideAnimate]="state"> + <div class="vnfs"> + <div class="vnf" *ngFor="let item of vnfsData" (click)="graphicShow()"> + <img src="../../../assets/images/VNF3.png" alt="VNF3"> + <h3>Mfvs_MMEManaxxdeafsfdf</h3> + <div class="intro"> + Mfvs_MMEMa naxxdfdafa fafad a afeaf sfdff doafe oghaiod aaafeageageagfdavzvdagewag + </div> + </div> + <div class="empty" *ngFor="let empty of emptys"></div> + </div> + <div class="pages"> + <nz-pagination [(nzPageIndex)]="current" [nzTotal]="500" [nzSize]="'small'" [nzPageSize]="10" [nzPageSizeOptions]="[10,15,20,25,30]" nzShowSizeChanger nzShowQuickJumper></nz-pagination> + </div> +</div> +<div [@showHideAnimate]="state2"> + <app-graphiclist (detailData)="detailShow($event)"></app-graphiclist> +</div> +<div [@showHideAnimate]="state3"> + <app-details [detailId]="detailId"></app-details> +</div> diff --git a/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.less b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.less new file mode 100644 index 00000000..977ef5c0 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.less @@ -0,0 +1,115 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.select { + margin-bottom: 20px; + span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; + } + nz-dropdown { + vertical-align: middle; + :hover{ + border-color: #147dc2; + } + button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; + span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; + } + i { + position: absolute; + top: 10px; + right: 10px; + } + } + //下拉框中的样式在style.less中,下拉框是在body中额外临时生成的 + } + .submit { + margin-left: 20px; + vertical-align: middle; + height: 30px; + padding: 0 10px; + span { + color: #fff; + font-weight: 400; + } + } +} + +.content { + background-color: #fff; + border-radius: 5px; + padding: 12px; + .vnfs { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + .vnf { + // width: 180px; + width: 18%; + height: 200px; + margin: 5px; + padding: 20px; + border-radius: 2px; + text-align: center; + cursor: pointer; + transition: all 0.3s linear; + &:hover { + background-color: #f5f5f5; + transform: scale(1.02); + } + h3 { + font-size: 14px; + color: #3fa8eb; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 0; + } + .intro { + text-align: left; + font-size: 12px; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + word-wrap:break-word; + word-break:break-all; + } + } + .empty { + // width: 180px; + width: 18%; + height: 200px; + margin: 5px; + border-radius: 2px; + } + } + .pages { + height: 25px; + margin: 20px 10px; + position: relative; + nz-pagination { + float: right; + } + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.spec.ts b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.spec.ts new file mode 100644 index 00000000..c535b737 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PerformanceVnfComponent } from './performance-vnf.component'; + +describe('PerformanceVnfComponent', () => { + let component: PerformanceVnfComponent; + let fixture: ComponentFixture<PerformanceVnfComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PerformanceVnfComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PerformanceVnfComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.ts b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.ts new file mode 100644 index 00000000..d2e8d991 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance-vnf/performance-vnf.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit, HostBinding } from '@angular/core'; +import { slideToRight, showHideAnimate } from '../../animates'; + +@Component({ + selector: 'app-performance-vnf', + templateUrl: './performance-vnf.component.html', + styleUrls: ['./performance-vnf.component.less'], + animations: [ slideToRight, showHideAnimate ] +}) +export class PerformanceVnfComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + constructor() { } + + ngOnInit() { + let _this = this; + setTimeout(function(){ + // 在路由切换时加载图片造成动画卡顿,先完成动画再加载图片 + _this.vnfsData = [ + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"}, + {name:"aaa",text:"oahgieango"} + ]; + _this.emptys = new Array(15-_this.vnfsData.length); + },300) + } + + // 筛选框(下拉框) + sourceNameList = ['aaaa','bbbb','cccc','dddddDDDDDDDDDDDDDDD']; + sourceNameSelected = this.sourceNameList[0]; + ReportingEntityNameList = ['aaaa','bbbb','cccc','ddddd']; + ReportingEntityNameSelected = this.ReportingEntityNameList[0]; + choseSourceName(item){ + console.log(item); + this.sourceNameSelected = item; + } + choseReportingEntityName(item){ + console.log(item); + this.ReportingEntityNameSelected = item; + } + + submit(){ + + } + // vnfs数据 + vnfsData = []; + emptys = []; //补空盒子用 + // 分页 + current = 1; //当前页码 + + //详情页标题显示 + graphicshow = false; + detailshow = false; + // 显示隐藏动画 + state = "show"; + state2 = "hide"; + state3 = "hide"; + performanceShow() { + this.state = 'show'; + this.state2 = 'hide'; + this.state3 = 'hide'; + this.graphicshow = false; + this.detailshow = false; + } + graphicShow() { + this.state = 'hide'; + this.state2 = 'show'; + this.state3 = 'hide'; + this.graphicshow = true; + this.detailshow = false; + } + // 选中id + detailId:number; + detailShow(prems) { + this.state = 'hide'; + this.state2 = 'hide'; + this.state3 = 'show'; + this.graphicshow = true; + this.detailshow = true; + console.log(prems); + this.detailId = prems.id; + } + + +} diff --git a/usecaseui-portal/src/app/performance/performance.component.css b/usecaseui-portal/src/app/performance/performance.component.css new file mode 100644 index 00000000..cf63a829 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance.component.css @@ -0,0 +1,26 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} diff --git a/usecaseui-portal/src/app/performance/performance.component.html b/usecaseui-portal/src/app/performance/performance.component.html new file mode 100644 index 00000000..c092e876 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance.component.html @@ -0,0 +1,17 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> Performance </h3> +<hr> diff --git a/usecaseui-portal/src/app/performance/performance.component.less b/usecaseui-portal/src/app/performance/performance.component.less new file mode 100644 index 00000000..2b1949a5 --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance.component.less @@ -0,0 +1,11 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} diff --git a/usecaseui-portal/src/app/performance/performance.component.spec.ts b/usecaseui-portal/src/app/performance/performance.component.spec.ts new file mode 100644 index 00000000..1bdc919d --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PerformanceComponent } from './performance.component'; + +describe('PerformanceComponent', () => { + let component: PerformanceComponent; + let fixture: ComponentFixture<PerformanceComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PerformanceComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PerformanceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/performance/performance.component.ts b/usecaseui-portal/src/app/performance/performance.component.ts new file mode 100644 index 00000000..12405e9e --- /dev/null +++ b/usecaseui-portal/src/app/performance/performance.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-performance', + templateUrl: './performance.component.html', + styleUrls: ['./performance.component.less'] +}) +export class PerformanceComponent implements OnInit { + + constructor() { } + + ngOnInit() { + + } + +} diff --git a/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.css b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.css new file mode 100644 index 00000000..a1cc6455 --- /dev/null +++ b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.css @@ -0,0 +1,59 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.list { + background-color: #fff; + border-radius: 5px; + padding: 10px; +} +.list nz-table tbody td span.onboarding { + font-size: 12px; + color: #147dc2; +} +.list nz-table tbody td span.onboarded { + font-size: 14px; + color: #147dc2; +} +.list nz-table tbody td span.updating { + font-size: 12px; + color: blue; +} +.list nz-table tbody td span.deleting { + font-size: 12px; + color: red; +} +.list nz-table tbody td span.invalid { + font-size: 14px; + color: purple; +} +.list nz-table tbody td i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; +} +.list nz-table tbody td i.anticon:hover { + color: #147dc2; +} diff --git a/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.html b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.html new file mode 100644 index 00000000..d5286a3a --- /dev/null +++ b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.html @@ -0,0 +1,61 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> Onboard VNF </h3> +<hr> +<div class="list"> + <nz-table #nzTable [nzData]="tableData" + nzShowSizeChanger + [nzFrontPagination]="false" + [nzShowQuickJumper]="true" + [nzPageSizeOptions]="[5,10,15,20]" + [nzTotal]= 'total' + [(nzPageSize)]="pageSize" + [(nzPageIndex)]='pageIndex' + [nzLoading]="loading" + nzSize="middle" + (nzPageIndexChange)="searchData()" + (nzPageSizeChange)="searchData(true)"> + <thead (nzSortChange)="sort($event)" nzSingleSort> + <tr> + <th nzWidth="5%">NO</th> + <th nzWidth="20%" nzShowSort nzSortKey="name"> Name </th> + <th nzWidth="20%">Type</th> + <th nzWidth="15%">Version</th> + <th nzWidth="20%">Status</th> + <th nzWidth="15%">Action</th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> --> + <tr *ngFor="let item of nzTable.data; let i = index; "> + <td>{{i+1}}</td> + <td>{{item.name}}</td> + <td>{{item.type}}</td> + <td>{{item.version}}</td> + <td> + <span [ngClass]="{'onboarding':item.status=='Onboarding','onboarded':item.status=='Onboarded', + 'updating':item.status=='Updating','deleting':item.status=='Deleting','invalid':item.status=='Invalid'}">{{item.status}}</span> + <nz-progress *ngIf="item.status!='Onboarded' && item.status!='Invalid'" [nzPercent]="item.progress"></nz-progress> + </td> + <td> + <i class="anticon anticon-cloud-upload-o" (click)="updataService()"></i> + <i class="anticon anticon-delete" (click)="deleteService()"></i> + </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> +</div>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.less b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.less new file mode 100644 index 00000000..ab118737 --- /dev/null +++ b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.less @@ -0,0 +1,50 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.list { + background-color: #fff; + border-radius: 5px; + padding: 10px; + nz-table { + tbody { + td { + span.onboarding { + font-size: 12px; + color: #147dc2; + } + span.onboarded { + font-size: 14px; + color: #147dc2; + } + span.updating { + font-size: 12px; + color: blue; + } + span.deleting { + font-size: 12px; + color: red; + } + span.invalid { + font-size: 14px; + color: purple; + } + i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; + &:hover{ + color: #147dc2; + } + } + } + } + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts new file mode 100644 index 00000000..0e49f656 --- /dev/null +++ b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OnboardVnfVmComponent } from './onboard-vnf-vm.component'; + +describe('OnboardVnfVmComponent', () => { + let component: OnboardVnfVmComponent; + let fixture: ComponentFixture<OnboardVnfVmComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ OnboardVnfVmComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(OnboardVnfVmComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.ts b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.ts new file mode 100644 index 00000000..c42b3ce3 --- /dev/null +++ b/usecaseui-portal/src/app/services/onboard-vnf-vm/onboard-vnf-vm.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit, HostBinding } from '@angular/core'; +import { MyhttpService } from '../../myhttp.service'; +import { slideToRight } from '../../animates'; + +@Component({ + selector: 'app-onboard-vnf-vm', + templateUrl: './onboard-vnf-vm.component.html', + styleUrls: ['./onboard-vnf-vm.component.less'], + animations: [ slideToRight ] +}) +export class OnboardVnfVmComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + constructor(private myhttp: MyhttpService) { } + + ngOnInit() { + this.getTableData(); + } + + + //表格数据 + tableData = []; + pageIndex = 1; + pageSize = 10; + total = 100; + loading = false; + sortName = null; + sortValue = null; + getTableData(){ + // 查询参数: 当前页码,每页条数,排序方式 + let paramsObj = { + pageIndex:this.pageIndex, + pageSize:this.pageSize, + nameSort:this.sortValue + } + this.myhttp.getOnboardTableData(paramsObj) + .subscribe((data)=>{ + console.log(data); + this.total = data.body.total; + this.tableData = data.body.tableList; + },(err)=>{ + console.log(err); + }) + } + sort(sort: { key: string, value: string }): void { + console.log(sort); + this.sortName = sort.key; + this.sortValue = sort.value; + this.getTableData(); + } + searchData(reset:boolean = false){ + console.log(reset) + this.getTableData(); + } + updataService(){ + console.log("updataService!"); + } + deleteService(){ + console.log("deleteService!"); + } + +} diff --git a/usecaseui-portal/src/app/services/services-list/services-list.component.css b/usecaseui-portal/src/app/services/services-list/services-list.component.css new file mode 100644 index 00000000..eaf8fddb --- /dev/null +++ b/usecaseui-portal/src/app/services/services-list/services-list.component.css @@ -0,0 +1,132 @@ +/* + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.action { + margin-bottom: 20px; +} +.action span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; +} +.action nz-dropdown { + vertical-align: middle; +} +.action nz-dropdown :hover { + border-color: #147dc2; +} +.action nz-dropdown button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; +} +.action nz-dropdown button span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; +} +.action nz-dropdown button i { + position: absolute; + top: 10px; + right: 10px; +} +.action .create { + float: right; + height: 30px; + padding: 0 10px; +} +.action .create span { + color: #fff; + font-weight: 400; +} +.list { + background-color: #fff; + border-radius: 5px; + padding: 10px; +} +.list nz-table tbody td span.active { + font-size: 14px; + color: #147dc2; +} +.list nz-table tbody td span.closed { + font-size: 14px; + color: red; +} +.list nz-table tbody td span.onboarding { + font-size: 12px; + color: #147dc2; +} +.list nz-table tbody td span.updating { + font-size: 12px; + color: blue; +} +.list nz-table tbody td span.deleting { + font-size: 12px; + color: red; +} +.list nz-table tbody td span.creating { + font-size: 12px; + color: green; +} +.list nz-table tbody td i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; +} +.list nz-table tbody td i.anticon:hover { + color: #147dc2; +} +.list nz-table tbody tr.childtr td { + font-size: 12px; + color: #147dc2; +} +.detailComponent { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100vh; + background-color: #f3f3f3; + overflow-y: auto; + padding: 20px 32px; + z-index: 3; +} +.createComponent { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100vh; + background-color: #f3f3f3; + overflow-y: auto; + padding: 20px 32px; + z-index: 3; +} diff --git a/usecaseui-portal/src/app/services/services-list/services-list.component.html b/usecaseui-portal/src/app/services/services-list/services-list.component.html new file mode 100644 index 00000000..e0866524 --- /dev/null +++ b/usecaseui-portal/src/app/services/services-list/services-list.component.html @@ -0,0 +1,185 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<h3 class="title"> Services List </h3> +<hr> +<div class="action"> + <span>Customer: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{customerSelected.name}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu style="max-height: 200px; overflow: auto;"> + <li nz-menu-item (click)="choseCustomer(item)" *ngFor="let item of customerList"> + <a title="{{item.name}}" style="max-width: 165px; overflow: hidden; text-overflow: ellipsis;">{{item.name}}</a> + </li> + </ul> + </nz-dropdown> + + + <span>Service Type: </span> + <nz-dropdown [nzTrigger]="'click'" [nzPlacement]="'bottomLeft'"> + <button nz-button nz-dropdown><span>{{serviceTypeSelected.name}}</span> <i class="anticon anticon-down"></i></button> + <ul nz-menu style="max-height: 200px; overflow: auto;"> + <li nz-menu-item (click)="choseServiceType(item)" *ngFor="let item of serviceTypeList"> + <a title="{{item.name}}" style="max-width: 165px; overflow: hidden; text-overflow: ellipsis;">{{item.name}}</a> + </li> + </ul> + </nz-dropdown> + + <button class="create" nz-button [nzType]="'primary'" (click)="showModal()"><i class="anticon anticon-plus-circle-o"></i><span> Create </span></button> + <nz-modal [(nzVisible)]="isVisible" nzTitle="Create" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()"> + <p>Content one</p> + <p>Content two</p> + <p>Content three</p> + </nz-modal> + <button class="create" nz-button [nzType]="'primary'" (click)="showModal2()"><i class="anticon anticon-plus-circle-o"></i><span> Create </span></button> + <nz-modal [(nzVisible)]="isVisible2" nzTitle="Create" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk2()"> + <span style="display:inline-block;width:70px;">Service: </span> + <nz-select style="width: 165px;" [(ngModel)]="templateTypeSelected" nzAllowClear (ngModelChange)="choseTemplateType()"> + <!-- <nz-option *ngFor="let item of templateType" [nzValue]="item" [nzLabel]="item"></nz-option> --> + <nz-option nzValue="SOTN" nzLabel="SOTN"></nz-option> + <nz-option nzValue="CCVPN" nzLabel="CCVPN"></nz-option> + </nz-select> + + <hr> + <span>SOTN VPN: </span> + <nz-select style="width: 165px;" [(ngModel)]="template1" nzAllowClear > + <nz-option *ngFor="let item of templates" [nzValue]="item" [nzLabel]="item.name"></nz-option> + </nz-select> + + <span> SITE: </span> + <nz-select style="width: 165px;" [(ngModel)]="template2" nzAllowClear > + <nz-option *ngFor="let item of templates" [nzValue]="item" [nzLabel]="item.name"></nz-option> + </nz-select> + + <div *ngIf="templateTypeSelected == 'CCVPN'"> + <br> + <span style="display:inline-block;width:70px;">SD-WAN: </span> + <nz-select style="width: 165px;" [(ngModel)]="template3" nzAllowClear > + <nz-option *ngFor="let item of templates" [nzValue]="item" [nzLabel]="item.name"></nz-option> + </nz-select> + </div> + </nz-modal> +</div> +<div class="list"> + <nz-table *ngIf="1" + #nzTable [nzData]="tableData" + nzShowSizeChanger + [nzFrontPagination]="false" + [nzShowQuickJumper]="true" + [nzPageSizeOptions]="[5,10,15,20]" + [nzTotal]= 'total' + [(nzPageSize)]="pageSize" + [(nzPageIndex)]='pageIndex' + [nzLoading]="loading" + [nzSize]="'middle'" + [nzScroll]="{ y: '58vh' }" + (nzPageIndexChange)="searchData()" + (nzPageSizeChange)="searchData(true)"> + <thead (nzSortChange)="sort($event)" nzSingleSort> + <tr> + <th nzWidth="5%">NO</th> + <th nzWidth="5%"></th> + <th nzWidth="20%" nzShowSort nzSortKey="name">Service Instance Id</th> + <th nzWidth="20%">Name</th> + <th nzWidth="15%">Type</th> + <th nzWidth="20%">Status</th> + <th nzWidth="15%">Action</th> + </tr> + </thead> + <tbody> + <ng-template ngFor let-data [ngForOf]="nzTable.data" let-i="index"> + <tr> + <td>{{i+1}}</td> + <td [nzShowExpand]="data.children[0]" [(nzExpand)]="data.expand"></td> + <td>{{data.serviceId}}</td> + <td>{{data.name}}</td> + <td>{{data.type}}</td> + <td> + <span [ngClass]="{'active':data.status=='Active','closed':data.status=='Closed','onboarding':data.status=='Onboarding', + 'updating':data.status=='Updating','deleting':data.status=='Deleting','creating':data.status=='Creating'}">{{data.status}}</span> + <nz-progress *ngIf="data.status!='Active' && data.status!='Closed'" [nzPercent]="data.progress"></nz-progress> + </td> + <td> + <i class="anticon anticon-setting" (click)="scaleService()"></i> + <i class="anticon anticon-cloud-upload-o" (click)="updataService()"></i> + <i class="anticon anticon-delete" (click)="deleteService()"></i> + </td> + </tr> + <tr class="childtr" [nzExpand]="data.expand" *ngFor="let item of data.children"> + <td></td> + <td></td> + <td>{{item.serviceId}}</td> + <td>{{item.name}}</td> + <td colspan="3">{{item.type}}</td> + </tr> + </ng-template> + </tbody> + </nz-table> + <nz-table *ngIf="1" + #nzTable2 [nzData]="tableData2" + nzShowSizeChanger + [nzFrontPagination]="true" + [nzShowQuickJumper]="true" + [nzPageSizeOptions]="[5,10,15,20]" + [(nzPageSize)]="pageSize" + [(nzPageIndex)]='pageIndex' + nzSize="middle" + [nzScroll]="{ y: '58vh' }"> + <thead nzSingleSort> + <tr> + <th nzWidth="5%">NO.</th> + <th nzWidth="20%"> Instance ID </th> + <th nzWidth="20%">Instance Name</th> + <!-- <th nzWidth="10%">Type</th> --> + <th nzWidth="25%">Description</th> + <th nzWidth="15%">Status</th> + <th nzWidth="10%">Action</th> + </tr> + </thead> + <tbody> + <!-- <ng-template ngFor let-data [ngForOf]="nzTable2.data" let-i="index"> --> + <tr *ngFor="let item of nzTable2.data; let i = index; "> + <td>{{pageSize*(pageIndex-1) + i+1}}</td> + <td>{{item.sotnvpnSer['service-instance-id']}}</td> + <td>{{item.sotnvpnSer['service-instance-name']}}</td> + <!-- <td>{{item.type}}</td> --> + <td>{{item.sotnvpnSer.description}}</td> + <td> + <span *ngIf="item.sotnvpnSer.status!='creating' && item.sotnvpnSer.status!='deleting'">{{item.sotnvpnSer.status}}</span> + <span *ngIf="item.sotnvpnSer.status=='creating' || item.sotnvpnSer.status=='deleting'" [ngClass]="{'deleting':item.sotnvpnSer.status=='deleting','creating':item.sotnvpnSer.status=='creating'}">{{item.sotnvpnSer.status}}</span> + <nz-progress *ngIf="item.sotnvpnSer.status=='creating' || item.sotnvpnSer.status=='deleting'" [nzPercent]="item.sotnvpnSer.rate"></nz-progress> + </td> + <td> + <span title="detail" class="action" [ngClass]="{'cannotclick':item.sotnvpnSer.status=='deleting'||item.sotnvpnSer.status=='creating'}" + (click)="showDetail(item)"> <i class="anticon anticon-ellipsis"></i> </span> + <span title="delete" class="action" [ngClass]="{'cannotclick':item.sotnvpnSer.status=='deleting'||item.sotnvpnSer.status=='creating'}" + (click)="deleteInstace(item)"> <i class="anticon anticon-delete"></i> </span> + </td> + </tr> + <!-- </ng-template> --> + </tbody> + </nz-table> +</div> + +<div class="detailComponent" *ngIf="detailshow"> + <app-ccvpn-detail [namesTranslate]="namesTranslate" [detailParams]="detailData" (closeDetail)="detailshow = false;"></app-ccvpn-detail> +</div> +<div class="createComponent" *ngIf="createshow"> + <app-ccvpn-creation + [createParams]="createData" + [namesTranslate]="namesTranslate" + (closeCreate)="closeCreate($event)"> + </app-ccvpn-creation> +</div> diff --git a/usecaseui-portal/src/app/services/services-list/services-list.component.less b/usecaseui-portal/src/app/services/services-list/services-list.component.less new file mode 100644 index 00000000..7e8ff80e --- /dev/null +++ b/usecaseui-portal/src/app/services/services-list/services-list.component.less @@ -0,0 +1,127 @@ +.title { + font: 700 18px/18px "思源黑体"; + color: #4c5e70; + margin-bottom: 18px; +} +hr { + border: none; + height: 2px; + background-color: #dce1e7; + margin-bottom: 20px; +} +.action { + margin-bottom: 20px; + span { + display: inline-block; + font: 700 14px "Arial"; + color: #4c5e70; + } + nz-dropdown { + vertical-align: middle; + :hover{ + border-color: #147dc2; + } + button { + width: 165px; + height: 30px; + background-color: #eceff4; + text-align: left; + border-color: #9fa9ab; + span { + font-weight: 400; + display: inline-block; + width: 120px; + overflow: hidden; + text-overflow: ellipsis; + padding-top: 2px; + } + i { + position: absolute; + top: 10px; + right: 10px; + } + } + //下拉框中的样式在style.less中,下拉框是在body中额外临时生成的 + } + .create { + float: right; + height: 30px; + padding: 0 10px; + span { + color: #fff; + font-weight: 400; + } + } +} +.list { + background-color: #fff; + border-radius: 5px; + padding: 10px; + nz-table { + tbody { + td { + span.active { + font-size: 14px; + color: #147dc2; + } + span.closed { + font-size: 14px; + color: red; + } + span.onboarding{ + font-size: 12px; + color: #147dc2; + } + span.updating{ + font-size: 12px; + color: blue; + } + span.deleting { + font-size: 12px; + color: red; + } + span.creating { + font-size: 12px; + color: green; + } + i.anticon { + cursor: pointer; + font-size: 18px; + padding: 2px; + &:hover{ + color: #147dc2; + } + } + } + tr.childtr { + td { + font-size: 12px; + color: #147dc2; + } + } + } + } +} + +.detailComponent { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100vh; + background-color: #f3f3f3; + overflow-y: auto; + padding: 20px 32px; + z-index: 3; +} +.createComponent { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100vh; + background-color: #f3f3f3; + overflow-y: auto; + padding: 20px 32px; + z-index: 3; +} diff --git a/usecaseui-portal/src/app/services/services-list/services-list.component.spec.ts b/usecaseui-portal/src/app/services/services-list/services-list.component.spec.ts new file mode 100644 index 00000000..61440dc3 --- /dev/null +++ b/usecaseui-portal/src/app/services/services-list/services-list.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ServicesListComponent } from './services-list.component'; + +describe('ServicesListComponent', () => { + let component: ServicesListComponent; + let fixture: ComponentFixture<ServicesListComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ServicesListComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ServicesListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/services/services-list/services-list.component.ts b/usecaseui-portal/src/app/services/services-list/services-list.component.ts new file mode 100644 index 00000000..d893070d --- /dev/null +++ b/usecaseui-portal/src/app/services/services-list/services-list.component.ts @@ -0,0 +1,511 @@ +import { Component, OnInit, HostBinding } from '@angular/core'; +import { MyhttpService } from '../../myhttp.service'; +import { slideToRight } from '../../animates'; +import { NzModalService } from 'ng-zorro-antd'; + +@Component({ + selector: 'app-services-list', + templateUrl: './services-list.component.html', + styleUrls: ['./services-list.component.less'], + animations: [ slideToRight ] +}) +export class ServicesListComponent implements OnInit { + @HostBinding('@routerAnimate') routerAnimateState; + constructor(private myhttp: MyhttpService, private modalService: NzModalService) { } + + ngOnInit() { + this.getallCustomers(); + this.getTemplateSubTypes(); + this.inputNamests(); + } + // 筛选框(下拉框)customer servicetype + customerList = []; + customerSelected = {name:null,id:null}; + serviceTypeList = []; + serviceTypeSelected = {name:null,id:null}; + + // 获取所有customer + getallCustomers(){ + this.myhttp.getAllCustomers() + .subscribe((data)=>{ + this.customerList = data.map((item)=>{return {name:item["subscriber-name"],id:item["global-customer-id"]}}); + this.customerSelected = this.customerList[0]; + this.choseCustomer(this.customerSelected); + // console.log(this.customers) + }) + } + + choseCustomer(item){ + this.customerSelected = item; + this.myhttp.getServiceTypes(this.customerSelected) + .subscribe((data)=>{ + this.serviceTypeList = data.map((item)=>{return {name:item["service-type"]}}); + this.serviceTypeSelected = this.serviceTypeList[0]; + this.choseServiceType(this.serviceTypeSelected); + // console.log(this.listServiceTypes); + }) + } + choseServiceType(item){ + this.serviceTypeSelected = item; + this.getTableData(); + } + + // 模态框(对话框) create + isVisible = false; + showModal(): void { + this.isVisible = true; + } + handleOk(): void { + console.log('Button ok clicked!'); + this.isVisible = false; + } + handleCancel(): void { + console.log('Button cancel clicked!'); + this.isVisible = false; + this.isVisible2 = false; + } + + // 创建模态框2(对话框) create ------------------------------- + isVisible2 = false; + showModal2(): void { + this.isVisible2 = true; + this.templates1 = []; //多次创建会push累积名字,从新置空 + this.templates2 = []; + this.templates3 = []; + this.getAlltemplates(); + } + // 服务 + templateTypeSelected = "SOTN"; + choseTemplateType(){ + // this.filterTemplates();//分类 + } + // 模板 + templates = []; templates1;templates2;templates3; + template1={name:null}; + template2={name:null}; + template3={name:null}; + // 模板分类数据,创建、获取实例分类共用 + templateSubTypes = {}; //子类,sotnvpn、site、sdwan + getTemplateSubTypes(){ + this.myhttp.getServicesCategory() + .subscribe((data)=>{ + this.templateSubTypes = data; + },(err)=>{ + console.log("getTemplateTypes err") + }) + } + + getAlltemplates(){ //获取所有模板类型 + this.myhttp.getAllServiceTemplates() + .subscribe((data)=>{ + console.log(data) + this.templates = data; + this.template1 = data[0]; + this.template2 = data[1]; + this.template3 = data[2]; + // this.filterTemplates();//分类 + },(err)=>{ + + }) + } + // filterTemplates(){ //模板类型分类,本地配置文件 + // this.templates1 = []; + // this.templates2 = []; + // this.templates3 = []; + // this.templates.forEach((item)=>{ + // this.templateSubTypes[this.templateTypeSelected].sotnvpn.find((d)=>{ + // return d["model-invariant-id"] == item.uuid && d["model-version-id"] == item.invariantUUID + // })?this.templates1.push(item):null; + // this.templateSubTypes[this.templateTypeSelected].site.find((d)=>{ + // return d["model-invariant-id"] == item.uuid && d["model-version-id"] == item.invariantUUID + // })?this.templates2.push(item):null; + // if(this.templateTypeSelected=="CCVPN"){ + // this.templateSubTypes[this.templateTypeSelected].sdwan.find((d)=>{ + // return d["model-invariant-id"] == item.uuid && d["model-version-id"] == item.invariantUUID + // })?this.templates3.push(item):null; + // } + // }) + // this.template1 = this.templates1[0]; + // this.template2 = this.templates2[0]; + // if(this.templates3[0]){ + // this.template3 = this.templates3[0]; + // } + // } + + + // 确定、取消 + createshow = false; + createData:Object={}; + handleOk2(): void { + console.log('Button ok clicked!'); + this.isVisible2 = false; + let data1 = {commonParams:{customer:this.customerSelected, serviceType:this.serviceTypeSelected, templateType:"SOTN"},templates:{template1:this.template1,template2:this.template2}}; + let data2 = {commonParams:{customer:this.customerSelected, serviceType:this.serviceTypeSelected, templateType:"CCVPN"},templates:{template1:this.template1,template2:this.template2,template3:this.template3}}; + + this.createData = this.templateTypeSelected == "SOTN" ? data1 : data2; + this.createshow = true; + } + // handleCancel(): void { + // console.log('Button cancel clicked!'); + // this.isVisible2 = false; + // } + + + //表格数据 + tableData = []; + pageIndex = 1; + pageSize = 10; + total = 100; + loading = false; + sortName = null; + sortValue = null; + getTableData(){ + // 查询参数: customer serviceType 当前页码,每页条数,排序方式 + let paramsObj = { + customer:this.customerSelected, + serviceType:this.serviceTypeSelected, + pageIndex:this.pageIndex, + pageSize:this.pageSize, + serviceIdSort:this.sortValue + } + this.myhttp.getServicesTableData(paramsObj) + .subscribe((data)=>{ + console.log(data); + this.total = data.body.total; + this.tableData = data.body.tableList; + },(err)=>{ + console.log(err); + }) + } + sort(sort: { key: string, value: string }): void { + console.log(sort); + this.sortName = sort.key; + this.sortValue = sort.value; + this.getTableData(); + } + searchData(reset:boolean = false){ + console.log(reset) + this.getTableData(); + } + + scaleService(){ + console.log("scaleService!"); + } + updataService(){ + console.log("updataService!"); + } + deleteService(){ + console.log("deleteService!"); + } + + //表格数据 + tableData2 = []; + getTableData2(){ + let params = { + customerId:this.customerSelected.id, + serviceType:this.serviceTypeSelected + } + this.myhttp.getInstanceTableData(params) + .subscribe((data)=>{ + this.pageIndex = 1; + this.tableData2 = []; + console.log(data) + // data.results.forEach((item)=>{ + // item["sotnvpnSer"] = item["service-subscription"]["service-instances"]["service-instance"].find((d)=>{ + // return this.templateSubTypes["SOTN"].sotnvpn.find((m)=>{ + // return d["model-invariant-id"]==m["model-invariant-id"] && d["model-version-id"]==m["model-version-id"] + // })?item["Type"]="SOTN":null || this.templateSubTypes["CCVPN"].sotnvpn.find((m)=>{ + // return d["model-invariant-id"]==m["model-invariant-id"] && d["model-version-id"]==m["model-version-id"] + // })?item["Type"]="CCVPN":null + // }) + + // if(item["sotnvpnSer"]){ + // this.tableData2.push(item); + // } + // }) + + //---------数据结构有问题,模拟只有一组数据情况---------// + data["sotnvpnSer"] = data["service-instance"].find((d)=>{ + return this.templateSubTypes["SOTN"].sotnvpn.find((m)=>{ + return d["model-invariant-id"]==m["model-invariant-id"] && d["model-version-id"]==m["model-version-id"] + })?d["Type"]="SOTN":null || this.templateSubTypes["CCVPN"].sotnvpn.find((m)=>{ + return d["model-invariant-id"]==m["model-invariant-id"] && d["model-version-id"]==m["model-version-id"] + })?d["Type"]="CCVPN":null + }) + let inputParams = JSON.parse(data["sotnvpnSer"]["input-parameters"]).service.parameters.requestInputs; + let descriptionName = Object.keys(inputParams).find((item)=>{ return item.endsWith("_description")}); + data["sotnvpnSer"]["description"] = inputParams[descriptionName]; + data["sotnvpnSer"]["status"] = "Active"; + this.tableData2.push(data); + + console.log(this.tableData2) + },(err)=>{ + console.log(err); + }) + } + + // 显示详情 + detailshow = false; + detailData:Object; + showDetail(service){ + service["siteSer"]=[]; + service["sdwanSer"]=[]; + service["customer"]=this.customerSelected; + service["serviceType"] = this.serviceTypeSelected; + // service["service-subscription"]["service-instances"]["service-instance"].forEach((item)=>{ + // this.templateSubTypes[service.Type].site.find((d)=>{ + // return d["model-invariant-id"] == item["model-invariant-id"] && d["model-version-id"] == item["model-version-id"] + // })?service["siteSer"].push(item):null; + // if(service.Type=="CCVPN"){ + // this.templateSubTypes[service.Type].sdwan.find((d)=>{ + // return d["model-invariant-id"] == item["model-invariant-id"] && d["model-version-id"] == item["model-version-id"] + // })?service["sdwanSer"].push(item):null; + // } + // }) + service["service-instance"].forEach((item)=>{ + this.templateSubTypes[service.sotnvpnSer.Type].site.find((d)=>{ + return d["model-invariant-id"] == item["model-invariant-id"] && d["model-version-id"] == item["model-version-id"] + })?service["siteSer"].push(item):null; + if(service.sotnvpnSer.Type=="CCVPN"){ + this.templateSubTypes[service.sotnvpnSer.Type].sdwan.find((d)=>{ + return d["model-invariant-id"] == item["model-invariant-id"] && d["model-version-id"] == item["model-version-id"] + })?service["sdwanSer"].push(item):null; + } + }) + this.detailshow = true; + this.detailData = service; + console.log(service); + } + // 删除 确认模态框 + deleteInstace(service){ + // 创建确认框 + this.modalService.confirm({ + nzTitle : 'Are you sure delete this instance?', + nzContent : `Instance ID: <b class="deleteModelContent"> ${service.sotnvpnSer["service-instance-id"]}</b>`, + nzOkText : 'Yes', + nzOkType : 'danger', + nzOnOk : () => { + console.log(service); + let allprogress = {}; //所有进度值,以operationId为键 + let querypros = []; //所有查询 + service.sotnvpnSer.rate = 0; + service.sotnvpnSer.status = "deleting"; + // let deletePros = service["service-subscription"]["service-instances"]["service-instance"].map((item)=>{ + // let id = item["service-instance-id"]; + // return new Promise((res,rej)=>{ + // this.myhttp.deleteInstance(id) + // .subscribe((data)=>{ + // let obj = {serviceId:id,operationId:data.operationId} + // let updata = (prodata)=>{ + // allprogress[prodata.operationId] = prodata.progress; + // let average = ((arr)=>{return eval(arr.join("+"))/arr.length})(Object.values(allprogress)); + // service.sotnvpnSer["rate"]=average; + // } + // querypros.push(this.queryProgress(obj,updata)); + // res(); + // }) + // }) + // }) + let deletePros = service["service-instance"].map((item)=>{ + let params = { + globalSubscriberId:this.customerSelected.id, + serviceType:this.serviceTypeSelected, + serviceInstanceId:item["service-instance-id"] + } + return new Promise((res,rej)=>{ + this.myhttp.deleteInstance(params) + .subscribe((data)=>{ + let obj = {serviceId:params.serviceInstanceId,operationId:data.operationId} + let updata = (prodata)=>{ + allprogress[prodata.operationId] = prodata.progress; + let average = ((arr)=>{return eval(arr.join("+"))/arr.length})(Object.values(allprogress)); + service.sotnvpnSer["rate"]=average; + } + querypros.push(this.queryProgress(obj,updata)); + res(); + }) + }) + }) + console.log(deletePros) + Promise.all(deletePros).then(()=>{ + Promise.all(querypros).then((data)=>{ + console.log(data); + service.sotnvpnSer.rate = 100; + service.sotnvpnSer.status = "deleted"; + setTimeout(()=>{ + this.getTableData(); + },1000) + }) + }) + + }, + nzCancelText: 'No', + nzOnCancel : () => console.log('Cancel') + }); + } + + + closeCreate(obj){ + if(!obj){ + this.createshow = false; //关闭创建窗口 + return false; + } + this.createshow = false; //关闭创建窗口 + console.log(obj); + let newData; //主表格中新创建的服务数据 + let stageNum = 0; //不同阶段进度,用于后续服务进度相加; + // -------------------------------------------------------------------------- + // obj.groupbody.map((group)=>{ //所有创建 + // return this.createService(group) + // }) + // obj.sitebody.map((group)=>{ //所有创建 + // console.log(group) + // return this.createService(group) + // }) + // ----------------------------------------------------------------------------- + this.createService(obj.vpnbody).then((data)=>{ + console.log(data) + newData = { //主表格中新创建的服务数据 + 'service-instance-id':data["serviceId"], + 'service-instance-name':obj.vpnbody.service.name, + description:obj.vpnbody.service.description, + status:"creating", + rate:0, + } + this.tableData2 = [{sotnvpnSer:newData},...this.tableData2]; + let updata = (prodata)=>{ + newData.rate = Math.floor(prodata.progress/3); + } + let queryParams = {serviceId:data["serviceId"],operationId:data["operationId"]}; + return this.queryProgress(queryParams,updata); + }).then((data)=>{ + console.log(data); + stageNum = newData.rate; //阶段进度值更新; + let allprogress = {}; //所有进度值,以operationId为键 + let querypros = []; //所有查询 + let createPros = obj.groupbody.map((group)=>{ //所有创建 + return this.createService(group).then((data)=>{ + console.log(data); + let updata = (prodata)=>{ + allprogress[prodata.operationId] = prodata.progress; + let average = ((arr)=>{return eval(arr.join("+"))/arr.length})(Object.values(allprogress)) + newData.rate = Math.floor(average/3) + stageNum; + } + let queryParams = {serviceId:data["serviceId"],operationId:data["operationId"]}; + querypros.push(this.queryProgress(queryParams,updata)) + }) + }) + + return new Promise((res)=>{ + Promise.all(createPros).then(()=>{ //所有创建好之后querypros中查询进度才全都添加完毕 + Promise.all(querypros).then((data)=>{ + console.log(data); + res("site--begin"); + }) + }) + }) + }).then((data)=>{ + console.log(data); + stageNum = newData.rate; //阶段进度值更新; + let allprogress = {}; + let querypros = []; //所有查询 + let createPros = obj.sitebody.map((group)=>{ //所有创建 + return this.createService(group).then((data)=>{ + console.log(data); + let updata = (prodata)=>{ + allprogress[prodata.operationId] = prodata.progress; + let average =((arr)=>{return eval(arr.join("+"))/arr.length})(Object.values(allprogress)) + newData.rate = Math.floor(average/3) + stageNum; + } + let queryParams = {serviceId:data["serviceId"],operationId:data["operationId"]}; + querypros.push(this.queryProgress(queryParams,updata)) + }) + }) + console.log(createPros); + Promise.all(createPros).then(()=>{ //所有创建好之后querypros中查询进度才全都添加完毕 + Promise.all(querypros).then((data)=>{ + console.log(data); + newData.rate = 100; + newData.status = "completed"; + setTimeout(()=>{ + this.getTableData(); + },1000) + }) + }) + }) + + } + + createService(params){ + let mypromise = new Promise((res,rej)=>{ + this.myhttp.createInstance(params) + .subscribe((data)=>{ + + res(data.service); + }) + }) + return mypromise; + } + + queryProgress(obj,callback){ + let mypromise = new Promise((res,rej)=>{ + // let data = { + // operationStatus:{ + // "operationId": "XXXXXX", + // "operation": "create|delete|update|scale", + // "result": "finished|error|processing", + // "reason": "", + // "userId": "", + // "operationContent": "Be creating pop.", + // "progress": 0, + // "operateAt": "", + // "finishedAt": "" + // } + // } + let requery = ()=>{ + this.myhttp.getProgress(obj) + .subscribe((data)=>{ + if(data.operationStatus.progress==undefined){ + console.log(data); + setTimeout(()=>{ + requery(); + },5000) + return false; + } + if(data.operationStatus.progress < 100){ + callback(data.operationStatus); + setTimeout(()=>{ + requery(); + },5000) + }else { + res(data.operationStatus); + } + }) + // setTimeout(()=>{ + // console.log(data.operationStatus.progress) + // data.operationStatus.progress++; + // if(data.operationStatus.progress<100){ + // callback(data.operationStatus); + // requery() + // }else{ + // callback(data.operationStatus); + // res(data.operationStatus) + // } + // },100) + } + requery(); + }) + return mypromise; + } + + + // 名字转换参数匹配 --> 传给子组件用 + namesTranslate:Object; + inputNamests(){ + this.myhttp.inputNamesTransform() + .subscribe((data)=>{ + this.namesTranslate = data; + }) + } + +} diff --git a/usecaseui-portal/src/app/services/services.component.html b/usecaseui-portal/src/app/services/services.component.html new file mode 100644 index 00000000..c4fddfc0 --- /dev/null +++ b/usecaseui-portal/src/app/services/services.component.html @@ -0,0 +1,18 @@ +<!-- + Copyright (C) 2018 CMCC, Inc. and others. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<p> + services works! +</p> diff --git a/usecaseui-portal/src/app/services/services.component.less b/usecaseui-portal/src/app/services/services.component.less new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/usecaseui-portal/src/app/services/services.component.less diff --git a/usecaseui-portal/src/app/services/services.component.spec.ts b/usecaseui-portal/src/app/services/services.component.spec.ts new file mode 100644 index 00000000..2e76b9f9 --- /dev/null +++ b/usecaseui-portal/src/app/services/services.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ServicesComponent } from './services.component'; + +describe('ServicesComponent', () => { + let component: ServicesComponent; + let fixture: ComponentFixture<ServicesComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ServicesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ServicesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/services/services.component.ts b/usecaseui-portal/src/app/services/services.component.ts new file mode 100644 index 00000000..eec235b4 --- /dev/null +++ b/usecaseui-portal/src/app/services/services.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-services', + templateUrl: './services.component.html', + styleUrls: ['./services.component.less'] +}) +export class ServicesComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} |