Commit 03be830f authored by liuzheng712's avatar liuzheng712

feat: luna with angular4

parent 0974c120
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "WebTerminal"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"../node_modules/xterm/dist/xterm.css",
"../node_modules/layui-layer/dist/theme/default/layer.css",
"styles.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/tether/dist/js/tether.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"../node_modules/xterm/dist/xterm.js",
"../node_modules/layui-layer/dist/layer.js",
"../node_modules/socket.io-client/dist/socket.io.js",
"./assets/js/mstsc.js",
"./assets/js/keyboard.js",
"./assets/js/rle.js",
"./assets/js/client.js",
"./assets/js/canvas.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
yarn-error.log
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
src/assets/
# MyApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.3.2.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Before running the tests make sure you are serving the app via `ng serve`.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
/**
* Created by liuzheng on 7/17/16.
*/
var server = {};
var http = require('http');
var express = require('express');
var io = require('socket.io');
var pty = require('pty.js');
// var terminal = require('term.js');
var socket;
var term;
var buff = [];
server.run = function (options) {
// create shell process
term = pty.fork(
process.env.SHELL || 'sh',
[],
{
name: require('fs').existsSync('/usr/share/terminfo/x/xterm-256color')
? 'xterm-256color'
: 'xterm',
cols: 80,
rows: 24,
cwd: process.env.HOME
}
);
//
// // store term's output into buffer or emit through socket
term.on('data', function (data) {
return !socket ? buff.push(data) : socket.emit('data', data);
});
// console.log('Created shell with pty master/slave pair (master: %d, pid: %d)', term.fd, term.pid);
var app = express();
var server = http.createServer(app);
var apis = express.Router();
app.use("/", express.static(__dirname + '/dist/')); // 创建服务端
// app.use("/socket.io/", express.static(__dirname + '/api/')); // 创建服务端
// let term.js handle req/res
// app.use(terminal.middleware());
apis.route('/browser')
.post(function (req, res) {
// console.log(req);
// res.string('');
res.json({verified: true, csrf: "liuzheng"})
});
apis.route('/checklogin')
.post(function (req, res) {
res.json({logined: true, id: 1, username: "liuzheng", name: "liuzheng"})
})
.get(function (req, res) {
res.json({logined: true})
});
apis.route('/nav')
.get(function (req, res) {
res.json([{
"id": "File",
"name": "Server",
"children": [
{
"id": "NewConnection",
"href": "Aaaa",
"name": "New connection",
"disable": true
},
{
"id": "Connect",
"href": "Aaaa",
"name": "Connect",
"disable": true
},
{
"id": "Disconnect",
"click": "Disconnect",
"name": "Disconnect"
},
{
"id": "DisconnectAll",
"click": "DisconnectAll",
"name": "Disconnect all"
},
{
"id": "Duplicate",
"href": "Aaaa",
"name": "Duplicate",
"disable": true
},
{
"id": "Upload",
"href": "Aaaa",
"name": "Upload",
"disable": true
},
{
"id": "Download",
"href": "Aaaa",
"name": "Download",
"disable": true
},
{
"id": " Search",
"href": "Aaaa",
"name": "Search",
"disable": true
},
{
"id": "Reload",
"click": "ReloadLeftbar",
"name": "Reload"
}
]
}, {
"id": "View",
"name": "View",
"children": [
{
"id": "HindLeftManager",
"click": "HideLeft",
"name": "Hind left manager"
},
{
"id": "SplitVertical",
"href": "Aaaa",
"name": "Split vertical",
"disable": true
},
{
"id": "CommandBar",
"href": "Aaaa",
"name": "Command bar",
"disable": true
},
{
"id": "ShareSession",
"href": "Aaaa",
"name": "Share session (read/write)",
"disable": true
},
{
"id": "Language",
"href": "Aaaa",
"name": "Language",
"disable": true
}]
}, {
"id": "Help",
"name": "Help",
"children": [
{
"id": "EnterLicense",
"click": "EnterLicense",
"name": "Enter License"
},
{
"id": "Website",
"click": "Website",
"name": "Website"
},
{
"id": "BBS",
"click": "BBS",
"name": "BBS"
}]
}])
});
app.use("/api", apis);
// let server listen on the port
options = options || {};
server.listen(options.port || 3000);
// let socket.io handle sockets
io = io.listen(server, {log: false});
io.sockets.on('connection', function (s) {
// when connect, store the socket
socket = s;
// handme incoming data (client -> server)
socket.on('data', function (data) {
term.write(data);
});
socket.on('resize', function (data) {
term.resize(data[0], data[1]);
console.log(data)
});
// handle connection lost
socket.on('disconnect', function () {
socket = null;
});
// send buffer data to client
while (buff.length) {
socket.emit('data', buff.shift());
}
});
};
server.run({port: 3000});
console.log('Please open your browser with http://127.0.0.1:3000');
import { AppPage } from './app.po';
describe('my-app App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular/cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular/cli/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
This diff is collapsed.
{
"name": "luna",
"version": "0.1.0",
"license": "GPLv3",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^4.2.4",
"@angular/common": "^4.2.4",
"@angular/compiler": "^4.2.4",
"@angular/core": "^4.2.4",
"@angular/forms": "^4.2.4",
"@angular/http": "^4.2.4",
"@angular/platform-browser": "^4.2.4",
"@angular/platform-browser-dynamic": "^4.2.4",
"@angular/router": "^4.2.4",
"angular2-logger": "^0.6.0",
"body-parser": "^1.18.2",
"bootstrap": "^4.0.0-alpha.6",
"clipboard": "^1.7.1",
"core-js": "^2.4.1",
"directory-encoder": "^0.9.2",
"filetree-css": "^1.0.0",
"handlebars": "^4.0.11",
"jquery": "^3.2.1",
"layui-layer": "git+https://github.com/jumpserver/layer.git",
"ng2-cookies": "^1.0.12",
"pty.js": "^0.3.1",
"rxjs": "^5.4.2",
"socket.io": "^2.0.3",
"ssh-keygen": "^0.4.1",
"tether": "^1.4.0",
"xterm": "^2.9.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.2",
"@angular/compiler-cli": "^4.2.4",
"@angular/language-service": "^4.2.4",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.3.2",
"typescript": "~2.3.3"
}
}
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
{
"/api": {
"target": "http://localhost:7871",
"secure": false
},
"/socket.io/": {
"target": "http://127.0.0.1:7871",
"secure": false
},
"/rdp/socket.io/": {
"target": "http://localhost:9250",
"pathRewrite": {
"^/rdp": ""
},
"secure": false
}
}
<div class="container" *ngIf="User.logined">
<footer>
<p>© Company 2017</p>
</footer>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* footer
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import {AppService, DataStore, User} from '../../app.service';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css'],
providers: [AppService]
})
export class FooterComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
}
}
/* login form */
form {
top: 80px;
position: relative;
width: 300px;
margin: auto;
background: rgba(130, 130, 130, .3);
padding: 20px 22px;
border: 1px solid;
border-color: rgba(255, 255, 255, .4) rgba(60, 60, 60, .4) rgba(60, 60, 60, .4) rgba(255, 255, 255, .4);
z-index: 9;
}
form input, form button {
width: 250px;
border: 1px solid;
border-color: rgba(60, 60, 60, .35) rgba(60, 60, 60, .35) rgba(255, 255, 255, .5) rgba(80, 80, 80, .45);
background: rgba(0, 0, 0, .2) no-repeat;
padding: 8px 24px 8px 10px;
font: bold .875em/1.25em "Monaco", Roboto, sans-serif;
letter-spacing: .075em;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, .1);
margin-bottom: 19px;
}
form input:focus {
background-color: rgba(0, 0, 0, .4);
}
.from-group {
position: relative;
}
.form-control-feedback {
position: absolute;
color: #ffffff;
top: 2px;
right: 2px;
z-index: 2;
display: block;
width: 34px;
height: 34px;
line-height: 34px;
text-align: center;
pointer-events: none;
font-weight: 400;
font-size: 18px;
}
::-webkit-input-placeholder {
color: #ccc;
text-transform: uppercase;
}
::-moz-placeholder {
color: #ccc;
text-transform: uppercase;
}
:-ms-input-placeholder {
color: #ccc;
text-transform: uppercase;
}
form button[type=submit] {
width: 250px;
margin-bottom: 0;
color: greenyellow;
letter-spacing: .05em;
text-shadow: 0 1px 0 #133d3e;
text-transform: uppercase;
background: #225556;
border-top-color: #9fb5b5;
border-left-color: #608586;
border-bottom-color: #1b4849;
border-right-color: #1e4d4e;
cursor: pointer;
}
.form {
z-index: 9;
}
.black {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 5;
background-color: #000000;
}
/* login form end */
<div class="black"></div>
<canvas id="q"></canvas>
<div class="form">
<form #f="ngForm" (ngSubmit)="onSubmit(f)">
<div class="from-group">
<input type="email" id="username" placeholder="Email address" [(ngModel)]="User.username"
name="email" required autofocus pattern="^\w+([.-]?\w+)*@\w+([.-]?\w+)*(.\w{2,3})+$">
<span class="fa fa-user-secret form-control-feedback"></span>
</div>
<div class="from-group">
<input type="password" name="password" id="password" placeholder="password" [(ngModel)]="User.password" required
(ngEnter)="onSubmit(f)">
<span class="fa fa-key form-control-feedback"></span>
</div>
<button type="submit">{{loginBotton}}</button>
</form>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, HttpService, User} from '../../app.service';
import {NgForm} from '@angular/forms';
import {Router} from '@angular/router';
declare let jQuery: any;
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
providers: [AppService]
})
export class LoginComponent implements OnInit {
DataStore = DataStore;
User = User;
loginBotton = 'login to your account';
constructor(private _appService: AppService,
private _http: HttpService,
private _router: Router,
private _logger: Logger) {
this._logger.log('login.ts:LoginComponent');
}
onSubmit(f: NgForm) {
if (f.valid) {
this.login();
} else {
this._logger.error("the form not valid")
}
}
login() {
this._logger.log('service.ts:AppService,login');
DataStore.error['login'] = '';
this._logger.log(User);
if (User.username.length > 0 && User.password.length > 6 && User.password.length < 100) {
this._http.post('/api/checklogin', JSON.stringify(User)).map(res => res.json())
.subscribe(
data => {
User.logined = data.logined;
User.name = data.name;
User.username = data.username;
User.logined = data.logined;
},
err => {
this._logger.error(err);
User.logined = false;
this._router.navigate(['login']);
DataStore.error['login'] = '后端错误,请重试';
return '后端错误,请重试';
},
() => {
if (User.logined) {
if (jQuery.isEmptyObject(DataStore.Path)) {
this._router.navigate(['welcome']);
} else {
this._router.navigate([DataStore.Path['name'], DataStore.Path['res']]);
}
} else {
this._router.navigate(['login']);
DataStore.error['login'] = '请检查用户名和密码';
return '请检查用户名和密码';
}
// jQuery('angular2').show();
});
} else {
DataStore.error['login'] = '请检查用户名和密码';
return '请检查用户名和密码';
}
}
ngOnInit() {
jQuery('#form').fadeIn('slow');
// this._router.navigate(['login']);
// jQuery('nav').hide();
}
}
.nav {
display: block;
margin-top: 2px;
height: 30px
}
.nav ul {
list-style-type: none;
line-height: 24px;
}
.nav li {
display: inline-block;
}
.nav a {
color: #f0f0f1;
text-decoration: none;
padding: 6px 15px 6px 15px;
}
.nav a:hover {
color: #fff;
cursor: pointer
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown:hover {
background-color: #2d2828;
}
.dropdown-content {
display: none;
position: absolute;
background-color: black;
color: #c6bcbc;
padding: 4px 0;
z-index: 999;
float: none;
list-style: none;
line-height: normal;
direction: ltr;
width: auto;
top: auto;
left: 0px;
margin-left: 0px;
margin-top: 0px;
min-width: 150px;
}
.dropdown:hover .dropdown-content {
display: block;
}
.nav .dropdown-content li {
float: left;
display: flex;
}
.nav .dropdown-content li a {
padding: 6px 14px 6px 25px;
white-space: nowrap;
font-family: 'Roboto', sans-serif;
font-size: 13px;
font-weight: 300;
position: relative;
text-decoration: none;
min-width: 150px;
line-height: normal;
}
.nav .dropdown-content li a span {
float: right;
}
.dropdown-content li:hover {
background-color: #3a3333;
color: black;
}
.dropdown-content li.disabled:hover {
background-color: black;
}
.dropdown-content li.disabled a {
color: #c5babc;
}
.dropdown-content li.disabled a:hover {
cursor: default;
color: #c5babc;
}
<div class="nav" *ngIf="User.logined">
<ul>
<li><a [routerLink]="['Index']"><img src="assets/imgs/logo.png" height="26px"/></a>
</li>
<li *ngFor="let v of DataStore.Nav; let k = index " [ngClass]="{'dropdown': v.children}">
<a>{{v.name}}</a>
<ul [ngClass]="{'dropdown-content': v.children}">
<li *ngFor="let vv of v.children; let kk = index" [ngClass]="{'disabled': vv.disable}">
<a *ngIf="vv.href" [routerLink]="[vv.href]">{{vv.name}}</a>
<a id="{{vv.id}}" *ngIf="vv.click" (click)="click(vv.click)">{{vv.name}}</a>
</li>
</ul>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NavComponent } from './nav.component';
describe('NavComponent', () => {
let component: NavComponent;
let fixture: ComponentFixture<NavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NavComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, User, HttpService} from '../../app.service';
import {CleftbarComponent} from '../../ControlPage/cleftbar/cleftbar.component';
import {SshComponent} from '../../ControlPage/control/ssh/ssh.component';
import {RdpComponent} from '../../ControlPage/control/rdp/rdp.component';
import {NavList} from '../../ControlPage/control/control.component';
declare let layer: any;
declare let jQuery: any;
@Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css'],
})
export class NavComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
this.getnav()
}
ngOnInit() {
}
click(event) {
this._logger.debug('nav.ts:NavComponent,click', event);
switch (event) {
case "ReloadLeftbar": {
CleftbarComponent.Reload();
break
}
case "HideLeft": {
CleftbarComponent.Hide();
break
}
case "ShowLeft": {
CleftbarComponent.Show();
break
}
case "Copy": {
// this._appService.copy();
break
}
case"Disconnect": {
switch (NavList.List[NavList.Active].type) {
case "ssh": {
SshComponent.TerminalDisconnect(NavList.List[NavList.Active]);
break
}
case "rdp": {
RdpComponent.Disconnect(NavList.List[NavList.Active]);
break
}
default: {
//statements;
break;
}
}
break
}
case"DisconnectAll": {
SshComponent.TerminalDisconnectAll();
RdpComponent.DisconnectAll();
break
}
case "Website": {
window.open('http://www.jumpserver.org');
break
}
case "BBS": {
window.open('http://bbs.jumpserver.org');
break
}
case "EnterLicense": {
this.EnterLicense();
break
}
default: {
break
}
}
}
EnterLicense() {
layer.prompt({
formType: 2,
maxlength: 500,
title: 'Please Input Code',
scrollbar: false,
area: ['400px', '300px'],
moveOut: true,
moveType: 1
}, function (value, index) {
DataStore.socket.emit('key', value);
// layer.msg(value); //得到value
layer.close(index);
});
}
getnav() {
this._logger.log('getnav');
return this._http.get('/api/nav')
.map(res => res.json())
.subscribe(response => {
DataStore.Nav = response;
});
}
static Hide() {
jQuery("app-nav").hide()
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component';
describe('NotFoundComponent', () => {
let component: NotFoundComponent;
let fixture: ComponentFixture<NotFoundComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NotFoundComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {NavComponent} from '../nav/nav.component'
@Component({
selector: 'app-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.css']
})
export class NotFoundComponent implements OnInit {
constructor() {
}
ngOnInit() {
NavComponent.Hide()
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupComponent } from './popup.component';
describe('PopupComponent', () => {
let component: PopupComponent;
let fixture: ComponentFixture<PopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-popup',
templateUrl: './popup.component.html',
styleUrls: ['./popup.component.css']
})
export class PopupComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
/**
* filetree.css
* */
:root {
font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}
.filetree input[type="checkbox"] {
display: none;
}
.filetree ul {
height: 0;
overflow: hidden;
}
.filetree > li input:checked ~ ul {
height: auto;
}
.filetree li {
list-style: none;
cursor: pointer;
}
.filetree label {
padding-left: 33px;
line-height: 33px;
display: inline-block;
}
/**
* Icon
* */
.filetree label {
background-image: url('./icon.png');
background-repeat: no-repeat;
}
.filetree input[type="checkbox"] + label {
background-position: 0 0;
width: 100%;
height: 33px;
}
.filetree input[type="checkbox"]:checked + label {
background-position: 0 -33px;
width: 100%;
height: 30px;
}
@media screen and (-webkit-min-device-pixel-ratio: 1.0), screen and (min--moz-device-pixel-ratio: 1.0), screen and (-o-min-device-pixel-ratio: 100/100), screen and (min-device-pixel-ratio: 1.0), screen and (min-resolution: 1.0dppx) {
filetree label {
background-image: url('./icon.png');
-webkit-background-size: 33px 63px;
-moz-background-size: 33px 63px;
background-size: 33px 63px;
}
}
<div id="sidebar">
<app-search></app-search>
<ul class="filetree">
<li *ngFor="let hostGroup of HostGroups; let i = index ">
<input type="checkbox" id="hostgroup-{{i}}">
<label for="hostgroup-{{i}}">{{hostGroup.name}}</label>
<ul>
<li *ngFor="let host of hostGroup.children" (click)="Connect(host)">{{host.name}}</li>
</ul>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CleftbarComponent } from './cleftbar.component';
describe('CleftbarComponent', () => {
let component: CleftbarComponent;
let fixture: ComponentFixture<CleftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CleftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CleftbarComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore, HttpService} from '../../app.service';
import {SshComponent} from '../control/ssh/ssh.component';
import {RdpComponent} from "../control/rdp/rdp.component";
declare let layer: any;
declare let jQuery: any;
export class HostGroup {
name: string;
id: number;
children: Array<Host>;
}
export class Host {
name: string;
uuid: string;
type: string;
}
@Component({
selector: 'app-cleftbar',
templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.css'],
providers: [SshComponent, RdpComponent]
})
export class CleftbarComponent implements OnInit {
DataStore = DataStore;
HostGroups: Array<HostGroup>;
static Reload() {
}
static Hide() {
}
static Show() {
}
constructor(private _appService: AppService,
private _term: SshComponent,
private _rdp: RdpComponent,
private _http: HttpService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
this._http.get('/api/hostlist')
.map(res => res.json())
.subscribe(response => {
this.HostGroups = response;
});
}
Connect(host) {
console.log(host);
let username: string;
if (host.users.length > 1) {
let options = "";
for (let u of host.users) {
options += "<option value='" + u + "'>" + u + "</option>"
}
layer.open({
title: 'Please Choose a User',
scrollbar: false,
moveOut: true,
moveType: 1,
btn: ["确定", "取消"],
content: "<select id='selectuser'>" + options + "</select>",
yes: function (index, layero) {
username = jQuery("#selectuser").val();
layer.close(index);
},
btn2: function (index, layero) {
},
cancel: function () {
//右上角关闭回调
//return false 开启该代码可禁止点击该按钮关闭
}
});
} else if (host.users.length === 1) {
username = host.users[0]
}
if (username === "") {
return
}
if (host.type === 'ssh') {
jQuery("app-ssh").show();
jQuery("app-rdp").hide();
this._term.TerminalConnect(host, username);
} else {
jQuery("app-ssh").hide();
jQuery("app-rdp").show();
this._rdp.Connect(host, username);
}
}
}
<app-controlnav></app-controlnav>
<app-ssh></app-ssh>
<app-rdp></app-rdp>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlComponent } from './control.component';
describe('ControlComponent', () => {
let component: ControlComponent;
let fixture: ComponentFixture<ControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面
*
* 主管已连接的主机标签卡,各连接方式的web展现(WebTerminal、RDP、VNC等)
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
export class Term {
machine: string;
socket: any;
term: any;
}
export class Rdp {
machine: string;
token: string;
client: any;
}
export class View {
nick: string;
type: string;
edit: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
Rdp: Rdp;
Term: Term;
}
export let NavList: {
List: Array<View>;
Active: number;
} = {
List: [new View()],
Active: 0,
};
@Component({
selector: 'app-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css']
})
export class ControlComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}
#tabs {
height: 30px;
width: 100%
}
#tabs ul li.disconnected {
background-color: darkgray;
}
#tabs ul li.hidden {
display: none;
}
#tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
overflow-y: hidden;
overflow-x: auto;
width: 100%;
display: inline-flex;
position: absolute;
}
#tabs ul li {
display: inline-table;
width: 150px;
height: 30px;
position: relative;
box-sizing: content-box;
}
#tabs ul li.active {
box-sizing: border-box;
border-bottom: 3px solid #7f3f98 !important;
}
#tabs ul li span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: gray;
font-family: 'Roboto', sans-serif;
font-size: 13px;
text-decoration: none;
padding: 8px 20px 6px 15px;
cursor: default;
width: 115px;
height: 21px;
}
#tabs ul 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;
}
#tabs ul li.active a {
color: white;
}
#tabs ul li.active span {
padding: 5px 20px 4px 15px;
color: white;
height: 18px;
}
#tabs ul 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;
}
<div id="tabs">
<ul>
<li *ngFor="let m of NavList.List;let i = index"
[ngClass]="{'active':i==NavList.Active,'disconnected':!m.connected, 'hidden': m.closed != false}"
id="termnav-{{i}}" (click)="setActive(i)">
<span *ngIf="!m.edit" (dblclick)="m.edit=true;setActive(i)">{{m.nick}}</span>
<input *ngIf="m.edit" [(ngModel)]="m.nick" autofocus (blur)="m.edit=false" (keyup.enter)="m.edit=false"/>
<a class="close" (click)="close(m,i)">&times;</a>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlnavComponent } from './controlnav.component';
describe('ControlnavComponent', () => {
let component: ControlnavComponent;
let fixture: ComponentFixture<ControlnavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlnavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlnavComponent);
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 {NavList} from '../control.component'
import {SshComponent} from '../ssh/ssh.component'
import {RdpComponent} from '../rdp/rdp.component'
import {NavComponent} from "../../../BasicPage/nav/nav.component";
declare let jQuery: any;
@Component({
selector: 'app-controlnav',
templateUrl: './controlnav.component.html',
styleUrls: ['./controlnav.component.css']
})
export class ControlnavComponent implements OnInit {
setActive = ControlnavComponent.setActive;
NavList = NavList;
constructor() {
}
ngOnInit() {
}
static checkActive(index) {
let len = NavList.List.length;
if (len == 1) {
// 唯一一个
NavList.Active = 0;
} else if (len - 1 == index) {
// 删了最后一个
NavList.Active = len - 2;
} else {
NavList.Active = index;
}
ControlnavComponent.setActive(NavList.Active)
}
static setActive(index) {
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[index].hide = false;
NavList.Active = index;
if (NavList.List[index].type === "ssh") {
jQuery("app-ssh").show();
jQuery("app-rdp").hide()
} else if (NavList.List[index].type === 'rdp') {
jQuery("app-ssh").hide();
jQuery("app-rdp").show();
}
}
close(host, index) {
if (host.type === 'rdp') {
RdpComponent.Disconnect(host)
} else if (host.type === 'ssh') {
SshComponent.TerminalDisconnect(host)
}
NavList.List.splice(index, 1);
ControlnavComponent.checkActive(index)
}
}
#rdp {
width: 100%;
height: 100%;
padding: 15px;
}
#rdp > div, iframe {
width: 100%;
height: 100%;
}
iframe {
border: none;
}
<div id="rdp">
<div *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'disconnected':!m.connected,'hidden': m.hide,'hidden':m.type!='rdp'}" id="rdp-{{i}}">
<canvas id="canvas-{{i}}"></canvas>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RdpComponent } from './rdp.component';
describe('RdpComponent', () => {
let component: RdpComponent;
let fixture: ComponentFixture<RdpComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RdpComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RdpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* RDP页面
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {NavList, View, Rdp} from '../control.component';
declare let Mstsc: any;
@Component({
selector: 'app-rdp',
templateUrl: './rdp.component.html',
styleUrls: ['./rdp.component.css']
})
export class RdpComponent implements OnInit {
NavList = NavList;
constructor() {
}
ngOnInit() {
}
Connect(host, username) {
let id = NavList.List.length - 1;
let canvas = Mstsc.$("canvas-" + id);
canvas.style.display = 'inline';
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
NavList.List[id].nick = host.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = "rdp";
NavList.List[id].Rdp = new Rdp;
NavList.List[id].Rdp.token = host.token;
NavList.List[id].Rdp.machine = host.uuid;
NavList.List[id].Rdp.client = Mstsc.client.create(Mstsc.$("canvas-" + id));
NavList.List[id].Rdp.client.connect(host.token, "rdp/socket.io");
NavList.List.push(new View());
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[id].hide = false;
NavList.Active = id;
}
static Disconnect(host) {
host.connected = false;
// document.getElementById("templatesrc").remove();
}
static DisconnectAll() {
}
}
#term {
width: 100%;
height: 100%;
padding: 15px;
}
#term > div {
height:100%;
}
.terminal {
border: #000 solid 5px;
color: #f0f0f0;
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px;
white-space: nowrap;
display: inline-block;
width: 100%;
height: 100%;
background-color: black;
}
#term .terminal div span {
min-width: 12px;
}
.reverse-video {
color: #000;
background: #f0f0f0;
}
.termChangBar {
line-height: 1;
margin: 0 auto;
border: 1px solid #ffffff;
color: #fff;
background-color: #ffffff;
position: fixed;
right: 0;
top: 0;
}
.hidden {
display: none;
}
#liuzheng {
position: fixed;
top: 0;
left: 0;
z-index: -1;
font-size: 11px !important;
padding-bottom: 16px !important;
font-family: 'Monaco', iosevka !important;
}
<div id="term">
<div *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'disconnected':!m.connected,'hidden': m.hide,'hidden':m.type!='ssh'}" id="term-{{i}}">
</div>
</div>
<span id="liuzheng">liuzheng</span>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SshComponent } from './ssh.component';
describe('SshComponent', () => {
let component: SshComponent;
let fixture: ComponentFixture<SshComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SshComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SshComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* WebTerminal
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import * as io from 'socket.io-client';
import {Cookie} from 'ng2-cookies/ng2-cookies';
declare let jQuery: any;
declare let Terminal: any;
import {AppService, DataStore} from '../../../app.service';
import {NavList, View, Term} from '../control.component';
@Component({
selector: 'app-ssh',
templateUrl: './ssh.component.html',
styleUrls: ['./ssh.component.css']
})
export class SshComponent implements OnInit {
NavList = NavList;
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('SshComponent.ts:SshComponent');
}
ngOnInit() {
}
TerminalConnect(host,username) {
let socket = io.connect();
let cols = '80';
let rows = '24';
if (Cookie.get('cols')) {
cols = Cookie.get('cols');
}
if (Cookie.get('rows')) {
rows = Cookie.get('rows');
}
Cookie.set('cols', cols, 99, '/', document.domain);
Cookie.set('rows', rows, 99, '/', document.domain);
let id = NavList.List.length - 1;
NavList.List[id].nick = host.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = "ssh";
NavList.List[id].Term = new Term;
NavList.List[id].Term.machine = host.uuid;
NavList.List[id].Term.socket = socket;
NavList.List[id].Term.term = new Terminal({
cols: cols,
rows: rows,
useStyle: true,
screenKeys: true,
});
NavList.List.push(new View());
for (let m in NavList.List) {
NavList.List[m].hide = true;
}
NavList.List[id].hide = false;
NavList.Active = id;
// TermStore.term[id]['term'].on('title', function (title) {
// document.title = title;
// });
NavList.List[id].Term.term.open(document.getElementById('term-' + id), true);
NavList.List[id].Term.term.write('\x1b[31mWelcome to Jumpserver!\x1b[m\r\n');
socket.on('connect', function () {
socket.emit('login', "root");
socket.emit('machine', host.uuid);
NavList.List[id].Term.term.on('data', function (data) {
socket.emit('data', data);
});
socket.on('data', function (data) {
NavList.List[id].Term.term.write(data);
});
socket.on('disconnect', function () {
this.TerminalDisconnect(id);
// TermStore.term[id]["term"].destroy();
// TermStore.term[id]["connected"] = false;
});
window.onresize = function () {
let col = Math.floor(jQuery('#term').width() / jQuery('#liuzheng').width() * 8) - 3;
let row = Math.floor(jQuery('#term').height() / jQuery('#liuzheng').height()) - 5;
let rows = 24;
let cols = 80;
if (Cookie.get('rows')) {
rows = parseInt(Cookie.get('rows'));
}
if (Cookie.get('cols')) {
cols = parseInt(Cookie.get('cols'));
}
if (col < 80) col = 80;
if (row < 24) row = 24;
if (cols == col && row == rows) {
} else {
for (let tid in NavList.List) {
if (NavList.List[tid].connected) {
NavList.List[tid].Term.socket.emit('resize', [col, row]);
NavList.List[tid].Term.term.resize(col, row);
}
}
Cookie.set('cols', String(col), 99, '/', document.domain);
Cookie.set('rows', String(row), 99, '/', document.domain);
}
};
jQuery(window).resize();
});
}
static TerminalDisconnect(host) {
host.connected = false;
host.Term.socket.destroy();
host.Term.term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
static TerminalDisconnectAll() {
alert("TerminalDisconnectAll");
for (let i in NavList.List) {
SshComponent.TerminalDisconnect(i);
// TermStore.term[i]["connected"] = false;
// TermStore.term[i]["socket"].destroy();
// TermStore.term[i]["term"].write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
}
}
}
div, term-leftbar, term-body {
height: 100%;
width: 100%;
padding: 0;
}
div {
background-color: black;
margin: 0;
}
app-cleftbar {
background-color: white;
padding: 0;
}
app-control {
padding: 0;
}
<div class="container-fluid row">
<app-cleftbar class="col-md-2" ></app-cleftbar>
<app-control class="col-md-10" ></app-control>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlPageComponent } from './controlpage.component';
describe('ControlPageComponent', () => {
let component: ControlPageComponent;
let fixture: ComponentFixture<ControlPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlPageComponent);
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, DataStore, User} from '../app.service';
@Component({
selector: 'app-controllpage',
templateUrl: './controlpage.component.html',
styleUrls: ['./controlpage.component.css'],
providers: [AppService]
})
export class ControlPageComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor() {
}
ngOnInit() {
}
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
}
<input class="left-search" placeholder=" Search ..." maxlength="2048" name="q" autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="search()"
(ngModelChange)="modelChange($event)">
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchComponent } from './search.component';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SearchComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面的搜索框
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnChanges, Input} from '@angular/core';
import {Logger} from 'angular2-logger/core';
import {AppService, DataStore} from '../../app.service';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnChanges {
@Input() input;
q: string;
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('LeftbarComponent.ts:SearchBar');
}
ngOnChanges(changes) {
this.q = changes.input.currentValue;
}
modelChange($event) {
this._appService.Search(this.q)
}
search() {
this._appService.Search(this.q)
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IleftbarComponent } from './ileftbar.component';
describe('IleftbarComponent', () => {
let component: IleftbarComponent;
let fixture: ComponentFixture<IleftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ IleftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IleftbarComponent);
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 {Logger} from 'angular2-logger/core';
import {AppService, DataStore} from '../../app.service';
@Component({
selector: 'app-ileftbar',
templateUrl: './ileftbar.component.html',
styleUrls: ['./ileftbar.component.css']
})
export class IleftbarComponent implements OnInit {
constructor(private _appService: AppService,
private _logger: Logger) {
this._logger.log('nav.ts:NavComponent');
// this._appService.getnav()
}
ngOnInit() {
}
}
<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 { IndexPageComponent } from './index-page.component';
describe('IndexPageComponent', () => {
let component: IndexPageComponent;
let fixture: ComponentFixture<IndexPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ IndexPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IndexPageComponent);
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, User} from '../app.service';
@Component({
selector: 'app-index-page',
templateUrl: './index-page.component.html',
styleUrls: ['./index-page.component.css'],
providers: [AppService]
})
export class IndexPageComponent implements OnInit {
User = User;
constructor() {
}
ngOnInit() {
}
}
/**
* app路由
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {environment} from '../environments/environment';
import {IndexPageComponent} from './IndexPage/index-page.component';
import {NotFoundComponent} from './BasicPage/not-found/not-found.component';
import {LoginComponent} from './BasicPage/login/login.component';
import {ControlPageComponent} from './ControlPage/controlpage.component';
const appRoutes: Routes = [
// { path: 'crisis-center', component: CrisisListComponent },
{path: 'welcome', component: IndexPageComponent}, // <-- delete this line
{path: 'login', component: LoginComponent},
{path: 'control', component: ControlPageComponent},
{path: '', redirectTo: '/welcome', pathMatch: 'full'},
{path: '**', component: NotFoundComponent}
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{enableTracing: !environment.production} // <-- debugging purposes only
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {
}
app-nav {
font-family: 'Roboto', sans-serif;
font-size: 13px;
font-weight: 300;
position: absolute;
display: inline-block;
top: 0;
left: 0;
height: 30px;
width: 100%;
color: #323a4e;
background-color: #463e3e;
background-repeat: no-repeat;
background-position: 100% 50%;
z-index: 5;
transition: background 0.2s ease-out;
}
nav {
height: 30px;
width: 100%;
}
<app-nav></app-nav>
<nav></nav>
<router-outlet></router-outlet>
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!');
}));
});
/**
* 控制主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component} from '@angular/core';
import {AppService, HttpService} from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [AppService, HttpService],
// directives: [LeftbarComponent, TermComponent]
})
export class AppComponent {
// DataStore = DataStore;
}
/**
* Created by liuzheng on 2017/8/30.
*/
/**
* app 模块
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms'; // <-- NgModel lives here
import {Logger, Options, Level as LoggerLevel} from 'angular2-logger/core';
import {HttpModule} from '@angular/http';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {NavComponent} from './BasicPage/nav/nav.component';
import {LoginComponent} from './BasicPage/login/login.component';
import {FooterComponent} from './BasicPage/footer/footer.component';
import {IleftbarComponent} from './IndexPage/ileftbar/ileftbar.component';
import {SearchComponent} from './ControlPage/search/search.component';
import {CleftbarComponent} from './ControlPage/cleftbar/cleftbar.component';
import {ControlComponent} from './ControlPage/control/control.component';
import {ControlnavComponent} from './ControlPage/control/controlnav/controlnav.component';
import {RdpComponent} from './ControlPage/control/rdp/rdp.component';
import {SshComponent} from './ControlPage/control/ssh/ssh.component';
import {ControlPageComponent} from './ControlPage/controlpage.component';
import {IndexPageComponent} from './IndexPage/index-page.component';
import { NotFoundComponent } from './BasicPage/not-found/not-found.component';
import { PopupComponent } from './BasicPage/popup/popup.component';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpModule,
],
declarations: [
AppComponent,
NavComponent,
LoginComponent,
FooterComponent,
RdpComponent,
SshComponent,
SearchComponent,
IleftbarComponent,
CleftbarComponent,
ControlComponent,
ControlnavComponent,
ControlPageComponent,
IndexPageComponent,
NotFoundComponent,
PopupComponent
// HeroListComponent,
// CrisisListComponent,
],
bootstrap: [AppComponent],
providers: [
{provide: Options, useValue: {store: false, level: LoggerLevel.WARN}},
Logger
]
})
export class AppModule {
}
This diff is collapsed.
export const environment = {
production: true
};
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false
};
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** Evergreen browsers require these. **/
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
/**
* Required to support Web Animations `@angular/animation`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/***************************************************************************************************
* Zone JS is required by Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/**
* Date, currency, decimal and percent pipes.
* Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
*/
// import 'intl'; // Run `npm install --save intl`.
/**
* Need to import at least one locale-data with intl.
*/
// import 'intl/locale-data/jsonp/en';
/* You can add global styles to this file, and also import other style files */
/*
* Style tweaks
* --------------------------------------------------
*/
@font-face {
font-family: iosevka;
src: url('assets/fonts/iosevka-term-ss07-1.13.3/woff2/iosevka-term-ss07-light.woff2') format("woff2");
font-weight: normal;
}
html,
body {
overflow-x: hidden; /* Prevent scroll on narrow devices */
height: 100%;
width: 100%;
}
footer {
padding: 30px 0;
}
app-root {
height: 100%;
}
.terminal {
font-size: 11px !important;
padding-bottom: 16px !important;
font-family: 'Monaco', iosevka !important;
}
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare const __karma__: any;
declare const require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
id: string;
}
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"typeof-compare": true,
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true,
"no-access-missing-member": true,
"templates-use-public": true,
"invoke-injectable": true
}
}
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