Commit f0abd8f7 authored by ibuler's avatar ibuler

[Update] 修改代码结构

parent 65ccd3dd
......@@ -30,7 +30,7 @@ import {ChangLanWarningDialogComponent, RDPSolutionDialogComponent, FontDialogCo
import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dialog.service';
import {PluginModules} from './plugins/plugins';
import {TestPageComponent} from './test-page/test-page.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/asset-tree/asset-tree.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/connect/connect.component';
import {SftpComponent} from './elements/sftp/sftp.component';
......
import {Component, Input, OnInit, Inject, SimpleChanges, OnChanges, ElementRef, ViewChild} from '@angular/core';
import {NavList, View} from '../../pages/control/control/control.component';
import {AppService, HttpService, LogService, NavService} from '../../app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
import {connectEvt} from '../../globals';
import {MatDialog} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {ActivatedRoute} from '@angular/router';
import {SystemUser, TreeNode, Asset, Node} from '../../model';
import {TreeNode, ConnectEvt} from '../../model';
import * as jQuery from 'jquery/dist/jquery.min';
declare var $: any;
......@@ -34,7 +34,6 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
title: 'title'
}
},
};
pos = {left: '100px', top: '200px'};
hiddenNodes: any;
......@@ -153,6 +152,11 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
});
}
Connect(node: TreeNode) {
const evt = new ConnectEvt(node, 'asset');
connectEvt.next(evt);
}
rootNodeAddDom(ztree, callback) {
const tId = ztree.setting.treeId + '_tree_refresh';
const refreshIcon = '<a id=' + tId + ' class="tree-refresh">' +
......@@ -210,76 +214,6 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
}
Connect(node: TreeNode) {
switch (node.meta.type) {
case 'asset':
this.connectAsset(node);
break;
case 'remote_app':
this.connectRemoteApp(node);
break;
default:
alert('Unknown type: ' + node.meta.type);
}
}
connectAsset(node: TreeNode) {
const host = node.meta.asset as Asset;
this._http.getMyAssetSystemUsers(host.id).subscribe(systemUsers => {
let user: SystemUser;
if (systemUsers.length > 1) {
// 检查系统用户优先级,获取最高优先级的
user = this.checkPriority(systemUsers);
if (user) {
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset);
}
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: systemUsers}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of systemUsers) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset);
}
});
} else if (systemUsers.length === 1) {
user = systemUsers[0];
this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset);
} else {
alert('该主机没有授权登录用户');
}
});
}
connectRemoteApp(node: TreeNode) {
const user = node.meta.user as SystemUser;
return this.manualSetUserAuthLoginIfNeed(node, user, this.loginRemoteApp);
}
loginRemoteApp(node: TreeNode, user: SystemUser) {
const id = NavList.List.length - 1;
if (node) {
NavList.List[id].nick = node.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].remoteApp = node.id;
NavList.List[id].user = user;
NavList.List[id].type = 'rdp';
NavList.List.push(new View());
NavList.Active = id;
jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
}
connectFileManager() {
const host = this.rightClickSelectNode.meta.asset;
const id = NavList.List.length - 1;
......@@ -301,64 +235,6 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
this.Connect(host);
}
manualSetUserAuthLoginIfNeed(host: Asset, user: SystemUser, callback) {
if (user.login_mode !== 'manual' || user.protocol !== 'rdp') {
return callback(host, user);
}
user = Object.assign({}, user);
const dialogRef = this._dialog.open(ManualPasswordDialogComponent, {
height: '250px',
width: '500px',
data: {username: user.username}
});
dialogRef.afterClosed().subscribe(result => {
if (!result) {
return;
}
if (result.skip) {
return callback(host, user);
}
user.username = result.username;
user.password = result.password;
return callback(host, user);
});
}
loginAsset(host: Asset, user: SystemUser) {
const id = NavList.List.length - 1;
if (user) {
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh' || user.protocol === 'telnet') {
NavList.List[id].type = 'ssh';
} else if (user.protocol === 'rdp' || user.protocol === 'vnc') {
NavList.List[id].type = 'rdp';
}
NavList.List.push(new View());
NavList.Active = id;
jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
}
checkPriority(sysUsers: Array<SystemUser>) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
recurseParent(node) {
const parentNode = node.getParentNode();
if (parentNode && parentNode.pId) {
......@@ -435,65 +311,3 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
@Component({
selector: 'elements-manual-password-dialog',
templateUrl: 'manual-password-dialog.html',
})
export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
}
onSkip() {
this.data.skip = true;
this.dialogRef.close(this.data);
}
onSkipAll() {
this.data.skipAll = true;
this.dialogRef.close(this.data);
}
onNoClick() {
this.dialogRef.close();
}
onEnter() {
this.dialogRef.close(this.data);
}
ngOnInit(): void {
}
}
import {Component, Input, OnInit, Output, Inject, OnDestroy, EventEmitter} from '@angular/core';
import {connectEvt} from '../../globals';
import {AppService, HttpService, LogService, NavService} from '../../app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {SystemUser, TreeNode, Asset} from '../../model';
import {View} from '../content/model';
import * as jQuery from 'jquery/dist/jquery.min';
declare var $: any;
@Component({
selector: 'elements-connect',
templateUrl: './connect.component.html',
})
export class ElementConnectComponent implements OnInit, OnDestroy {
@Output() onNewView: EventEmitter<View> = new EventEmitter<View>();
pos = {left: '100px', top: '200px'};
hasLoginTo = false;
constructor(private _appSvc: AppService,
public _dialog: MatDialog,
public _logger: LogService,
private activatedRoute: ActivatedRoute,
private _http: HttpService,
) {
}
ngOnInit(): void {
connectEvt.asObservable().subscribe(evt => {
switch (evt.action) {
case 'asset': {
this.connectAsset(evt.node);
break;
}
}
});
}
ngOnDestroy(): void {
connectEvt.unsubscribe();
}
Connect(node: TreeNode) {
switch (node.meta.type) {
case 'asset':
this.connectAsset(node);
break;
case 'remote_app':
this.connectRemoteApp(node);
break;
default:
alert('Unknown type: ' + node.meta.type);
}
}
connectAsset(node: TreeNode) {
const host = node.meta.asset as Asset;
this._http.getMyAssetSystemUsers(host.id).subscribe(systemUsers => {
let user: SystemUser;
if (systemUsers.length > 1) {
// 检查系统用户优先级,获取最高优先级的
user = this.checkPriority(systemUsers);
if (user) {
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: systemUsers}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of systemUsers) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
});
} else if (systemUsers.length === 1) {
user = systemUsers[0];
this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
} else {
alert('该主机没有授权登录用户');
}
});
}
connectRemoteApp(node: TreeNode) {
const user = node.meta.user as SystemUser;
return this.manualSetUserAuthLoginIfNeed(node, user, this.loginRemoteApp.bind(this));
}
loginRemoteApp(node: TreeNode, user: SystemUser) {
if (node) {
const view = new View();
view.nick = node.name;
view.connected = true;
view.editable = false;
view.closed = false;
view.remoteApp = node.id;
view.user = user;
view.type = 'rdp';
this.onNewView.emit(view);
}
}
connectFileManager(node: TreeNode) {
const host = node.meta.asset as Asset;
if (host) {
const view = new View();
view.nick = '[FILE]' + host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.type = 'sftp';
this.onNewView.emit(view);
// jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
}
connectTerminal(node: TreeNode) {
this.Connect(node);
}
manualSetUserAuthLoginIfNeed(node: any, user: SystemUser, callback) {
if (user.login_mode !== 'manual' || user.protocol !== 'rdp') {
return callback(node, user);
}
user = Object.assign({}, user);
const dialogRef = this._dialog.open(ManualPasswordDialogComponent, {
height: '250px',
width: '500px',
data: {username: user.username}
});
dialogRef.afterClosed().subscribe(result => {
if (!result) {
return;
}
if (result.skip) {
return callback(node, user);
}
user.username = result.username;
user.password = result.password;
return callback(node, user);
});
}
loginAsset(host: Asset, user: SystemUser) {
if (user) {
const view = new View();
view.nick = host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.user = user;
if (user.protocol === 'ssh' || user.protocol === 'telnet') {
view.type = 'ssh';
} else if (user.protocol === 'rdp' || user.protocol === 'vnc') {
view.type = 'rdp';
}
this.onNewView.emit(view);
}
}
checkPriority(sysUsers: Array<SystemUser>) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
@Component({
selector: 'elements-manual-password-dialog',
templateUrl: 'manual-password-dialog.html',
})
export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
}
onSkip() {
this.data.skip = true;
this.dialogRef.close(this.data);
}
onSkipAll() {
this.data.skipAll = true;
this.dialogRef.close(this.data);
}
onNoClick() {
this.dialogRef.close();
}
onEnter() {
this.dialogRef.close(this.data);
}
ngOnInit(): void {
}
}
.window {
display: none;
/*padding: 15px;*/
}
li.disconnected {
background-color: darkgray;
}
li.hidden {
display: none;
}
li {
display: inline-table;
width: 150px;
height: 30px;
position: relative;
box-sizing: content-box;
float: left;;
background-color: #463a3a66;
border-right: 1px solid #3a3333c2;
}
li.active {
box-sizing: border-box;
border-bottom: 3px solid #19aa8d;
}
li span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #f1f0f0;
font-family: 'Roboto', sans-serif;
font-size: 13px;
text-decoration: none;
padding-left: 12px;
padding-right: 14px;
line-height: 26px;
cursor: default;
width: 115px;
height: 21px;
}
li a.close {
font-family: 'Roboto', sans-serif;
font-size: 13px;
position: absolute;
color: gray;
top: 0;
right: 5px;
cursor: pointer;
line-height: 26px;
display: inline-block;
}
li.active a {
color: white;
}
li.active span {
padding-left: 12px;
line-height: 26px;
color: white;
height: 18px;
}
li input {
font-family: 'Roboto', sans-serif;
font-size: 13px;
width: 115px;
border: none;
background-color: inherit;
color: white;
padding: 5px 20px 4px 15px;
height: 18px;
}
<li
[ngClass]="{'active': view.active,'disconnected': !view.connected, 'hidden': view.closed != false}"
[id]="view.id" (click)="setActive()" (dblclick)="view.editable=true;setActive()"
>
<span *ngIf="view.nick">{{view.nick | truncatechars:25 }}</span>
<input *ngIf="view.editable" [(ngModel)]="view.nick" (blur)="view.editable=false" (keyup.enter)="view.editable=false" autofocus="true"/>
<a class="close" (click)="close()">&times;</a>
</li>
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
import {View, ViewAction} from '../content/model';
@Component({
selector: 'elements-content-tab',
templateUrl: './content-tab.component.html',
styleUrls: ['./content-tab.component.css'],
})
export class ElementContentTabComponent implements OnInit {
@Input() view: View;
@Output() onAction: EventEmitter<ViewAction> = new EventEmitter<ViewAction>();
ngOnInit(): void {
}
close() {
const action = new ViewAction(this.view, 'close');
this.onAction.emit(action);
}
setActive() {
const action = new ViewAction(this.view, 'active');
this.onAction.emit(action);
}
}
div, elements-term, elements-guacamole, elements-settings {
height: 100%;
}
elements-term, elements-guacamole, elements-settings {
/*padding-bottom: 30px;*/
}
.window {
display: none;
/*padding: 15px;*/
}
.active {
display: block;
}
.right-side {
height: 100%;
width: 100%;
background-color: gray;
}
<div class="window" [ngClass]="{'active':view.active}" style="height: 100%">
<elements-ssh-term
[view]="view"
[host]="view.host"
[sysUser]="view.user"
*ngIf="view.type=='ssh'"
>
</elements-ssh-term>
<elements-guacamole
[host]="view.host"
[sysUser]="view.user"
[remoteAppId]="view.remoteApp"
*ngIf="view.type=='rdp'"
>
</elements-guacamole>
<app-sftp *ngIf="view.type=='sftp'" [host]="view.host"></app-sftp>
</div>
import {Component, OnInit, Input} from '@angular/core';
import {View} from '../content/model';
@Component({
selector: 'elements-content-window',
templateUrl: './content-window.component.html',
styleUrls: ['./content-window.component.css']
})
export class ElementContentViewComponent implements OnInit {
@Input() view: View;
static active() {
// viewList.List.forEach((v, k) => {
// v.hide = id.toString() !== k;
// });
// viewList.Active = id;
}
static TerminalDisconnect(id) {
// if (viewList.List[id].connected) {
// viewList.List[id].connected = false;
// viewList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
// TermWS.emit('logout', viewList.List[id].room);
// }
}
static RdpDisconnect(id) {
// viewList.List[id].connected = false;
}
static DisconnectAll() {
// for (let i = 0; i < viewList.List.length; i++) {
// Todo:
// ContentComponent.TerminalDisconnect(i);
// }
}
constructor() {
}
ngOnInit() {
}
// trackByFn(index: number, item: View) {
// return item.id;
// }
}
/*.right-side {*/
/*height: 100%;*/
/*width: 100%;*/
/*background-color: gray;*/
/*}*/
/*div {*/
/*height: 100%;*/
/*width: 100%;*/
/*padding: 0;*/
/*background-color: #1f1b1b;*/
/*margin: 0;*/
/*position: initial;*/
/*}*/
/*#content {*/
/*padding-top: 0;*/
/*}*/
/*.container-fluid {*/
/*padding-top: 30px;*/
/*}*/
.tabs {
height: 30px;
overflow-y: hidden;
overflow-x: hidden;
position: relative;
}
.tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
display: block;
min-width: 100%;
padding-left: 0;
}
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #676a6c;
}
.tabs::-webkit-scrollbar {
height: 2px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #F5F5F5;
}
.scroll-botton {
font-size: 20px;
float: left;
height: 30px;
overflow: hidden;
background-color: #3a3333;
color: white
}
<div id="content" fxLayout="column" ngxSplit="column" style="width: 100%;height: 100%;">
<div fxFlex="0 0 30px">
<div class="scroll-botton" style="padding: 0 5px">
<a class="left" (click)="scrollLeft()"><i class="fa fa-caret-left"></i></a>
<a class="right" (click)="scrollRight()"><i class="fa fa-caret-right"></i></a>
</div>
<div class="tabs">
<ul>
<!--<ul [ngStyle]="{'width':150*viewList.length+'px'}">-->
<elements-content-tab
*ngFor="let view of viewList"
[view]="view" (onAction)="onViewAction($event)">
</elements-content-tab>
</ul>
</div>
</div>
<div fxFlex="0 0 calc(100%-35px)" id="winContainer">
<div class="window" *ngFor="let view of viewList; let i=index">
<elements-content-window [view]="view"></elements-content-window>
</div>
</div>
</div>
<elements-connect (onNewView)="onNewView($event)" ></elements-connect>
/**
* 控制页面
*
* 主管已连接的主机标签卡,各连接方式的web展现(WebTerminal、RDP、VNC等)
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {TermWS} from '../../globals';
import {View, ViewAction} from './model';
import jQuery from 'jquery/dist/jquery.min';
@Component({
selector: 'elements-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.css']
})
export class ElementContentComponent implements OnInit {
viewList: Array<View> = [];
static RdpDisconnect(id) {
// viewList.List[id].connected = false;
}
static DisconnectAll() {
// for (let i = 0; i < viewList.List.length; i++) {
// // Todo:
// // ContentComponent.TerminalDisconnect(i);
// }
}
constructor() {
}
ngOnInit() {
}
onNewView(view) {
this.viewList.push(view);
this.setViewActive(view);
}
onViewAction(action: ViewAction) {
switch (action.name) {
case 'active': {
this.setViewActive(action.view);
break;
}
case 'close': {
this.closeView(action.view);
break;
}
}
}
setViewActive(view) {
this.viewList.forEach((v, k) => {
v.active = v === view;
});
}
closeView(view) {
let nextActiveView = null;
const index = this.viewList.indexOf(view);
if (view.active) {
// 如果关掉的是最后一个, 存在上一个
if (index === this.viewList.length - 1 && index !== 0) {
nextActiveView = this.viewList[index - 1];
} else if (index < this.viewList.length) {
nextActiveView = this.viewList[index + 1];
}
}
this.viewList.splice(index, 1);
if (nextActiveView) {
this.setViewActive(nextActiveView);
}
}
scrollLeft() {
const tabRef = jQuery('.tabs');
tabRef.scrollLeft(tabRef.scrollLeft() - 100);
}
scrollRight() {
const tabRef = jQuery('.tabs');
tabRef.scrollLeft(tabRef.scrollLeft() + 100);
}
}
export class View {
id: string;
nick: string;
type: string;
editable: boolean;
active: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
remoteApp: string;
room: string;
Rdp: any;
Term: any;
}
export class ViewAction {
view: View;
name: string;
constructor(view: View, name: string) {
this.view = view;
this.name = name;
}
}
// Elements
import {ElementTableComponent} from './table/table.component';
import {ElementLeftbarComponent} from './leftbar/leftbar.component';
import {ElementLeftBarComponent} from './left-bar/left-bar.component';
import {ElementContentComponent} from './content/content.component';
import {ElementContentViewComponent} from './content-window/content-window.component';
import {ElementContentTabComponent} from './content-tab/content-tab.component';
import {ElementAssetTreeComponent} from './asset-tree/asset-tree.component';
import {ElementTreeFilterComponent} from './tree-filter/tree-filter.component';
import {ElementOfooterComponent} from './ofooter/ofooter.component';
import {ElementFooterComponent} from './footer/footer.component';
import {ElementTermComponent} from './term/term.component';
......@@ -13,17 +18,23 @@ import {ElementIframeComponent} from './iframe/iframe.component';
import {ElementDialogAlertComponent} from './dialog/dialog.service';
import {ElementGuacamoleComponent} from './guacamole/guacamole.component';
import {ElementSshTermComponent} from './ssh-term/ssh-term.component';
import {ElementAssetTreeComponent, AssetTreeDialogComponent, ManualPasswordDialogComponent} from './asset-tree/asset-tree.component';
import {ElementConnectComponent, AssetTreeDialogComponent, ManualPasswordDialogComponent} from './connect/connect.component';
import {RDPSolutionDialogComponent, FontDialogComponent} from './nav/nav.component';
export const ElementComponents = [
ElementLeftbarComponent,
ElementLeftBarComponent,
ElementContentComponent,
ElementContentTabComponent,
ElementContentViewComponent,
ElementConnectComponent,
ElementTreeFilterComponent,
ElementOfooterComponent,
ElementTableComponent,
ElementFooterComponent,
ElementTermComponent,
ElementInteractiveComponent,
ElementNavComponent, ChangLanWarningDialogComponent,
ElementNavComponent,
ChangLanWarningDialogComponent,
ElementPopupComponent,
ElementRdpComponent,
ElementServerMenuComponent,
......@@ -32,6 +43,7 @@ export const ElementComponents = [
ElementGuacamoleComponent,
ElementAssetTreeComponent,
ElementSshTermComponent,
ElementConnectComponent,
AssetTreeDialogComponent,
ManualPasswordDialogComponent,
RDPSolutionDialogComponent,
......
<div class="sidebar" fxLayout="column" ngxSplit="column">
<div fxflex="1 1 30px" class="tree-filter">
<elements-tree-filter></elements-tree-filter>
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 calc(90%-60px)">
<elements-asset-tree [query]="q" ></elements-asset-tree>
</div>
<div class="footer-version" fxflex="1 1 30px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
//
//:root {
// font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
//}
label {
margin-bottom: 0;
}
.fa.fa-undefined:before {
content: "\f26c";
}
.tree-filter {
height: 30px;
}
.overflow {
height: 90%;
display: flex;
width: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a;
font-size: 9pt;
left: 0;
width: 100%;
padding: 1px 20px 0 20px;
border-top: 1px solid #e7eaec;
bottom: 0;
height: 60px;
}
.footer-version > p {
height: 8px;
font-size: 13px;
padding-top: 2px;
padding-bottom: 2px;
color: #d6cbcb;
}
//@import "~@swimlane/ngx-ui/release/styles/components/scrollbars";
.ngx-scroll-overlay {
overflow: auto; // for FF
}
//.sidebar::-webkit-scrollbar-track {
// -webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.3);
// background-color: #676a6c;
//}
//
//.sidebar::-webkit-scrollbar {
// width: 8px;
//}
//
//.sidebar::-webkit-scrollbar-thumb {
// background-color: #F5F5F5;
// border-radius: 6px;
// border: 2px solid transparent;
//}
import {Component, Inject, OnInit, ViewChild, ElementRef} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service';
// import {ElementTreeFilterComponent} from '../tree-filter/tree-filter.component';
import {DataStore} from '../../globals';
import {version} from '../../../environments/environment';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
export interface Node {
id: string;
name: string;
comment: string;
title: string;
isParent: boolean;
pId: string;
open: boolean;
iconSkin: string;
meta: object;
}
export class Host {
name: string;
id: string;
type: string;
}
@Component({
selector: 'elements-left-bar',
templateUrl: './left-bar.component.html',
styleUrls: ['./left-bar.component.scss'],
// providers: [ElementTreeFilterComponent]
})
export class ElementLeftBarComponent {
DataStore = DataStore;
version = version;
q: string;
event: MouseEvent;
clientX = 0;
clientY = 0;
TooltipPosition = 'above';
static Reload() {
}
static Hide() {
DataStore.leftbarshow = false;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'HideLeftManager') {
DataStore.Nav[i]['children'][key] = {
'id': 'ShowLeftManager',
'click': 'ShowLeft',
'name': 'Show left manager'
};
}
});
});
window.dispatchEvent(new Event('resize'));
}
static Show() {
DataStore.leftbarshow = true;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'ShowLeftManager') {
DataStore.Nav[i]['children'][key] = {
'id': 'HideLeftManager',
'click': 'HideLeft',
'name': 'Hide left manager'
};
}
});
});
window.dispatchEvent(new Event('resize'));
}
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: LogService,
public _dialog: MatDialog,
) {
this._logger.log('nav.ts:NavComponent');
}
Search(q) {
// Todo:
// this._search.Search(q);
}
onRightClick(event: MouseEvent): void {
this.clientX = event.clientX;
this.clientY = event.clientY;
}
}
<mat-sidenav-container>
<mat-sidenav #sidenav mode="side" opened="true" class="example-sidenav"
[fixedInViewport]="options.value.fixed" [fixedTopGap]="options.value.top"
[fixedBottomGap]="options.value.bottom">
<nav>
<div class="header">
<a href="http://www.jumpserver.org" target="_blank">
<img alt="image" height="55" src="/static/imgs/logo-text.png" style="margin-left: 10px">
</a>
</div>
<div class="body">
<ul class="nav metismenu nav-frist-level">
<li *ngFor="let bar of leftbar;let i = index" [ngClass]="{'active':i==active}">
<a (click)="gotoLink(bar.link,i,-1)">
<i class="{{bar.class}}"></i>
<span class="nav-label">{{bar.name|trans}}</span>
<span class="{{bar.label}}"></span>
</a>
<ul class="nav nav-second-level collapse">
<li *ngFor="let child of bar.child;let ii = index" [ngClass]="{'active':ii==active2}">
<a (click)="gotoLink(child.link,i,ii)">{{child.name|trans}}</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</mat-sidenav>
<div class="navbar-header">
<button class="navbar-minimalize minimalize-styl-2 btn btn-primary " (click)="sidenav.toggle()"><i
class="fa fa-bars"></i></button>
</div>
</mat-sidenav-container>
li a {
background-color: rgba(0, 0, 0, 0);
box-sizing: border-box;
color: rgb(167, 177, 194);
cursor: pointer;
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
font-weight: 600;
height: 46px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
padding-bottom: 14px;
padding-left: 25px;
padding-right: 20px;
padding-top: 14px;
position: relative;
text-align: left;
text-decoration-color: rgb(167, 177, 194);
text-decoration-line: none;
text-decoration-style: solid;
text-size-adjust: 100%;
width: 220px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active > a {
width: 216px;
color: white;
}
li.active ul.nav.nav-second-level li {;
border-bottom-style: none;
border-bottom-width: 0px;
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
height: 32px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
position: relative;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active ul.nav.nav-second-level {
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
margin-bottom: 0px;
margin-top: 0px;
padding-left: 0px;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-margin-after: 0px;
-webkit-margin-before: 0px;
-webkit-margin-end: 0px;
-webkit-margin-start: 0px;
-webkit-padding-start: 0px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
mat-sidenav {
background-color: #2f4050;
}
nav {
width: 220px;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
}
div.header {
background-color: #202c37;
}
div.body {
background-color: #2f4050;
height: 100%;
}
li:hover {
color: white !important;
}
.nav-second-level li a {
padding: 7px 10px 7px 52px;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementLeftbarComponent } from './leftbar.component';
describe('ElementLeftbarComponent', () => {
let component: ElementLeftbarComponent;
let fixture: ComponentFixture<ElementLeftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementLeftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementLeftbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {Router} from '@angular/router';
@Component({
selector: 'elements-leftbar',
templateUrl: './leftbar.component.html',
styleUrls: ['./leftbar.component.scss']
})
export class ElementLeftbarComponent implements OnInit {
leftbar = [
{
'name': 'Dashboard',
'class': 'fa fa-dashboard',
'label': '',
'link': '/'
},
{
'name': 'Users',
'class': 'fa fa-group',
'label': 'fa arrow',
'child': [
{
'name': 'User',
}, {
'name': 'User group',
}, {
'name': 'Login logs',
}
]
},
{
'name': 'Assets',
'class': 'fa fa-inbox',
'label': 'fa arrow',
'child': [
{
'name': 'Asset',
}, {
'name': 'Asset group',
}, {
'name': 'Cluster',
}, {
'name': 'Admin user',
}, {
'name': 'System user',
}, {
'name': 'Labels',
}
]
},
{
'name': 'Perms',
'class': 'fa fa-edit',
'label': 'fa arrow',
'child': [{'name': 'Asset permission'}]
},
{
'name': 'Sessions',
'class': 'fa fa-rocket',
'label': 'fa arrow',
'child': [
{
'name': 'Session online'
},
{
'name': 'Session offline'
},
{
'name': 'Commands'
},
{
'name': 'Terminal'
},
]
},
{
'name': 'Job Center',
'class': 'fa fa-coffee',
'label': 'fa arrow',
'child': [
{
'name': 'Task',
'link': '/task'
},
]
},
{
'name': 'Settings',
'class': 'fa fa-gears',
'label': '',
'link': '/luna/setting'
},
];
options: FormGroup;
active: number;
active2: number;
constructor(fb: FormBuilder,
private _router: Router) {
this.options = fb.group({
'fixed': true,
'top': 0,
'bottom': 0,
});
}
ngOnInit() {
this.active = 6;
this.active2 = 0;
}
gotoLink(link: string, index: number, index2: number) {
if (link) {
if (link === '/luna/setting') {
this._router.navigate(['setting']);
} else {
window.location.href = link;
}
}
this.active = index;
this.active2 = index2;
}
}
.nav {
display: block;
margin-top: 2px;
height: 30px
height: 31px;
background-color: #463e3e;
}
.nav ul {
......
import {AfterViewInit, Component, Input, OnInit, OnDestroy } from '@angular/core';
import {Terminal} from 'xterm';
import {NavList, View} from '../../pages/control/control/control.component';
import {View} from '../content/model';
import {UUIDService} from '../../app.service';
import {CookieService} from 'ngx-cookie-service';
import {Socket} from '../../utils/socket';
......@@ -14,6 +14,7 @@ import {getWsSocket} from '../../globals';
})
export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() host: any;
@Input() view: View;
@Input() sysUser: any;
@Input() index: number;
@Input() token: string;
......@@ -22,13 +23,11 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
secret: string;
ws: Socket;
roomID: string;
view: View;
constructor(private _uuid: UUIDService, private _cookie: CookieService) {
}
ngOnInit() {
this.view = NavList.List[this.index];
this.secret = this._uuid.gen();
this.newTerm();
getWsSocket().then(sock => {
......@@ -92,7 +91,7 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
// 服务器主动断开
this.ws.on('disconnect', () => {
console.log('On disconnect event trigger');
this.close();
this.view.connected = false;
});
this.ws.on('logout', data => {
......@@ -111,19 +110,15 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
});
}
// 客户端主动关闭
close() {
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
active() {
this.term.focus();
}
ngOnDestroy(): void {
this.close();
console.log('Close view');
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
}
......@@ -24,9 +24,6 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
@Output() winSizeChangeTrigger = new EventEmitter<Array<number>>();
winSizeChange$: Observable<any>;
constructor(private _cookie: CookieService) {
}
ngOnInit() {
this.winSizeChange$ = Observable.fromEvent(window, 'resize')
.debounceTime(500)
......@@ -66,21 +63,35 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
availableWidth = activeEle.width() - elementPaddingHor - (<any>this.term).viewport.scrollBarWidth;
}
const dimensions = (<any>this.term).renderer.dimensions;
const geometry = [
Math.floor(availableWidth / (<any>this.term).renderer.dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / (<any>this.term).renderer.dimensions.actualCellHeight) - 1
Math.floor(availableWidth / dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / dimensions.actualCellHeight) - 2
];
console.log(availableWidth, dimensions.actualCellWidth);
console.log(availableHeight, dimensions.actualCellHeight);
console.log('with: ', geometry[0], 'height: ', geometry[1]);
if (!isFinite(geometry[0])) {
geometry[0] = 80;
}
if (!isFinite(geometry[1])) {
geometry[1] = 24;
}
return geometry;
}
resizeTerm() {
const size = this.getWinSize();
// Todo: 修改大小
console.log('get size is: ', size);
if (isNaN(size[0]) || isNaN(size[1])) {
fit(this.term);
} else {
(<any>this.term).renderer.clear();
this.term.resize(size[0], size[1]);
}
console.log(size);
this.winSizeChangeTrigger.emit([this.term.cols, this.term.rows]);
}
......
/*.left-search {*/
/*padding-left: 14px;*/
/*width: 100%;*/
/*border: none;*/
/*}*/
.search {
border: none;
border-left-width: 0;
border-bottom: #19aa8d 1px inset;
width: 100%;
background: #2f2a2a;
color: #d6cbcb;
height: 30px;
padding-left: 10px;
/* padding-top: 30px;
//position: fixed;
//height: 28px;
*/
}
<input #keyword id="search" class="search"
placeholder=" {{'Search'| trans }} ..."
maxlength="2048"
name="q"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" [(ngModel)]="q"
>
import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service';
@Component({
selector: 'elements-tree-filter',
templateUrl: './tree-filter.component.html',
styleUrls: ['./tree-filter.component.css']
})
export class ElementTreeFilterComponent implements OnChanges {
q: string;
@Input() input;
searchRequest: any;
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: LogService) {
this._logger.log('LeftbarComponent.ts:SearchBar');
}
ngOnChanges(changes) {
this.q = changes.input.currentValue;
}
modelChange($event) {
this.Search(this.q);
}
public Search(q) {
if (this.searchRequest) {
this.searchRequest.unsubscribe();
}
this.searchRequest = this._http.search(q)
.subscribe(
data => {
this._logger.log(data);
},
err => {
this._logger.error(err);
},
() => {
}
);
this._logger.log(q);
}
}
@Pipe({name: 'SearchFilter'})
export class SearchFilter implements PipeTransform {
transform(value: any, input: string) {
if (input) {
input = input.toLowerCase();
return value.filter(function (el: any) {
// ToDo: search with a simple SQL like language, and a bug search a group's hosts
return JSON.stringify(el).toLowerCase().indexOf(input) > -1;
});
}
return value;
}
}
'use strict';
import {EventEmitter} from 'events/events';
import * as io from 'socket.io-client';
import * as neffos from 'neffos.js';
import {Terminal} from 'xterm';
// const abc = io.connect('/ssh');
import {Socket} from './utils/socket';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {ConnectEvt, User as _User } from './model';
import {DataStore as _DataStore, Browser as _Browser, Video as _Video, Monitor as _Monitor} from './model';
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws';
const port = document.location.port ? ':' + document.location.port : '';
const wsURL = scheme + '://' + document.location.hostname + port + '/socket.io/';
const hostname = document.location.hostname;
const wsURL = `${scheme}://${hostname}${port}/socket.io/`;
export let TermWS = null;
export const emitter = new(EventEmitter);
export const sep = '/';
export let Video: {
id: string,
src: string,
type: string,
height: number,
width: number,
json: object;
timelist: Array<number>;
totalTime: number;
} = {
id: '',
src: '',
type: '',
width: 0,
height: 0,
json: {},
timelist: [],
totalTime: 0,
};
export let Monitor: {
token: string,
room: string,
type: string,
} = {
token: '',
room: '',
type: 'term',
};
export class Group {
id: string;
name: string;
membercount: number;
comment: string;
}
export let User: {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array<Group>;
logined: boolean;
} = {
id: '',
name: 'nobody',
username: '',
password: '',
phone: '',
avatar: '',
role: '',
email: '',
wechat: '',
comment: '',
is_active: false,
is_superuser: false,
date_joined: '',
last_login: '',
date_expired: '',
groups: [],
logined: false,
};
export let DataStore: {
socket: any;
Nav: Array<object>;
NavShow: boolean;
Path: {};
error: {};
msg: {};
loglevel: number;
leftbarshow: boolean;
windowsize: Array<number>;
autologin: boolean;
guacamole_token: string;
guacamole_token_time: number;
} = {
export let Video = new _Video();
export let Monitor = new _Monitor();
export let User = new _User();
export const DataStore: _DataStore = {
socket: TermWS,
Nav: [{}],
NavShow: true,
......@@ -115,30 +31,8 @@ export let DataStore: {
guacamole_token: '',
guacamole_token_time: 0
};
export let CSRF = '';
export let Browser: {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
} = {
userAgent: navigator.userAgent,
appCodeName: navigator.appCodeName,
appName: navigator.appName,
appVersion: navigator.appVersion,
language: navigator.language,
platform: navigator.platform,
product: navigator.product,
productSub: navigator.productSub,
vendor: navigator.vendor,
};
export let Browser = new _Browser();
export const i18n = new Map();
export async function getWsSocket() {
......@@ -155,4 +49,4 @@ export async function getWsSocket() {
return TermWS;
}
export const connectEvt = new BehaviorSubject<ConnectEvt>(new ConnectEvt(null, null));
export class UserGroup {
id: string;
name: string;
comment: string;
}
export class User {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array <UserGroup>;
logined: boolean;
}
export class SystemUser {
id: string;
name: string;
......@@ -43,3 +69,72 @@ export class GuacObjAddResp {
code: number;
result: string;
}
export class ConnectEvt {
node: TreeNode;
action: string;
constructor(node: TreeNode, action: string) {
this.node = node;
this.action = action;
}
}
export class DataStore {
socket: any;
Nav: Array<object>;
NavShow = true;
Path: {};
error: {};
msg: {};
loglevel: number;
leftbarshow = true;
windowsize: Array<number>;
autologin: boolean;
guacamole_token: string;
guacamole_token_time: number;
}
export class Browser {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
constructor() {
this.userAgent = navigator.userAgent;
this.appCodeName = navigator.appCodeName;
this.appName = navigator.appName;
this.appVersion = navigator.appVersion;
this.language = navigator.language;
this.platform = navigator.platform;
this.product = navigator.product;
this.productSub = navigator.productSub;
this.vendor = navigator.vendor;
}
}
export class Video {
id: string;
src: string;
type: string;
height: number;
width: number;
json: object;
timelist: Array<number>;
totalTime: number;
}
export class Monitor {
token: string;
room: string;
type: string;
}
......@@ -15,8 +15,8 @@ import {NavList} from './control/control.component';
styleUrls: ['./control.component.css'],
})
export class PagesControlComponent implements OnInit {
DataStore = DataStore;
User = User;
dataStore = DataStore;
user = User;
constructor() {
}
......
......@@ -33,7 +33,7 @@
.tabs ul li.active {
box-sizing: border-box;
border-bottom: 3px solid #19aa8d !important;
/*border-bottom: 3px solid #19aa8d !important;*/
}
.tabs ul li span {
......
<div class="container" *ngIf="User.logined">
<div class="row row-offcanvas row-offcanvas-right">
<div class="col-12 col-md-9">
<p class="float-right hidden-md-up">
<button type="button" class="btn btn-primary btn-sm" data-toggle="offcanvas">Toggle nav</button>
</p>
<div class="jumbotron">
<h1>Hello, world! {{User.username}}</h1>
<p>This is an example to show the potential of an offcanvas layout pattern in Bootstrap. Try some
responsive-range viewport sizes to see it in action.</p>
</div>
<div class="row">
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
</div><!--/row-->
</div><!--/span-->
<div class="col-6 col-md-3 sidebar-offcanvas" id="sidebar">
<div class="list-group">
<a href="#" class="list-group-item active">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
</div>
</div><!--/span-->
</div><!--/row-->
<hr>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesIndexComponent } from './index.component';
describe('PagesIndexComponent', () => {
let component: PagesIndexComponent;
let fixture: ComponentFixture<PagesIndexComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesIndexComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesIndexComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService} from '../../app.service';
import {User} from '../../globals';
@Component({
selector: 'pages-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.css'],
})
export class PagesIndexComponent implements OnInit {
User = User;
constructor() {
}
ngOnInit() {
}
}
div {
height: 100%;
width: 100%;
padding: 0;
background-color: #1f1b1b;
margin: 0;
position: initial;
}
#container {
padding-top: 0;
}
.container-fluid {
padding-top: 30px;
}
<elements-nav></elements-nav>
<div id="container" class="container-fluid row" fxLayout="row" ngxSplit="row">
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.leftbarshow">
<elements-left-bar></elements-left-bar>
</div>
<div fxFlex="0" ngxSplitHandle [style.display]="activeViewIsRdp() ? 'none' : 'block'" (mouseup)="dragSplitBtn($event)"></div>
<div [fxFlex]="DataStore.leftbarshow ? '1 1 80%' : '1 1 100%'" ngxSplitArea class="content">
<elements-content></elements-content>
</div>
</div>
import {Component, OnInit} from '@angular/core';
import {DataStore, User} from '../../globals';
import {NavList} from '../control/control/control.component';
@Component({
selector: 'pages-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css'],
})
export class PageMainComponent implements OnInit {
User = User;
DataStore = DataStore;
ngOnInit(): void {
}
activeViewIsRdp() {
return NavList.List[NavList.Active].type === 'rdp';
}
dragSplitBtn(evt) {
window.dispatchEvent(new Event('resize'));
}
}
import {PageMainComponent} from './main/main.component';
import {PagesBlankComponent} from './blank/blank.component';
import {PagesConnectComponent} from './connect/connect.component';
import {PagesControlComponent} from './control/control.component';
import {PagesIndexComponent} from './index/index.component';
// import {PagesControlComponent} from './control/control.component';
import {PagesMonitorComponent} from './monitor/monitor.component';
import {PagesReplayComponent} from './replay/replay.component';
// import {PagesSettingComponent} from './setting/setting.component';
import {PagesNotFoundComponent} from './not-found/not-found.component';
import {PagesLoginComponent} from './login/login.component';
import {CleftbarComponent} from './control/cleftbar/cleftbar.component';
// import {CleftbarComponent} from './control/cleftbar/cleftbar.component';
import {JsonComponent} from './replay/json/json.component';
import {ControlComponent} from './control/control/control.component';
import {PagesControlNavComponent} from './control/control/controlnav/nav.component';
import {SearchComponent, SearchFilter} from './control/search/search.component';
// import {ControlComponent} from './control/control/control.component';
// import {PagesControlNavComponent} from './control/control/controlnav/nav.component';
// import {SearchComponent, SearchFilter} from './control/search/search.component';
import {PagesMonitorLinuxComponent} from './monitor/linux/linux.component';
import {PagesMonitorWindowsComponent} from './monitor/windows/windows.component';
import {ReplayGuacamoleComponent} from './replay/guacamole/guacamole.component';
export const PagesComponents = [
PageMainComponent,
PagesBlankComponent,
PagesConnectComponent,
PagesControlComponent, ControlComponent, PagesControlNavComponent,
CleftbarComponent,
PagesIndexComponent,
// PagesControlComponent, ControlComponent, PagesControlNavComponent,
// CleftbarComponent,
PagesMonitorComponent,
PagesReplayComponent, JsonComponent,
// PagesSettingComponent,
PagesNotFoundComponent,
PagesLoginComponent,
SearchComponent,
SearchFilter,
// SearchComponent,
// SearchFilter,
PagesMonitorLinuxComponent,
PagesMonitorWindowsComponent,
ReplayGuacamoleComponent
......
......@@ -12,6 +12,7 @@ import {PagesBlankComponent} from '../pages/blank/blank.component';
import {TestPageComponent} from '../test-page/test-page.component';
import {PagesConnectComponent} from '../pages/connect/connect.component';
import {PagesReplayComponent} from '../pages/replay/replay.component';
import {PageMainComponent} from '../pages/main/main.component';
import {PagesControlComponent} from '../pages/control/control.component';
import {PagesNotFoundComponent} from '../pages/not-found/not-found.component';
import {PagesMonitorComponent} from '../pages/monitor/monitor.component';
......@@ -25,7 +26,7 @@ const appRoutes: Routes = [
{path: 'connect', component: PagesConnectComponent},
{path: 'sftp', component: SftpComponent},
{path: 'undefined', component: PagesBlankComponent},
{path: '', component: PagesControlComponent},
{path: '', component: PageMainComponent},
{path: '**', component: PagesNotFoundComponent}
];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment