diff --git a/src/app/elements/asset-tree/asset-tree.component.scss b/src/app/elements/asset-tree/asset-tree.component.scss index 28b18ee4..e9e2442f 100644 --- a/src/app/elements/asset-tree/asset-tree.component.scss +++ b/src/app/elements/asset-tree/asset-tree.component.scss @@ -184,6 +184,10 @@ tr:hover { color: rgb(204, 204, 204); width: 20px; line-height: 30px; + &.fa-square-o { + font-weight: bold; + padding: 0 0 0 6px; + } } .tree-banner-icon:hover { diff --git a/src/app/elements/connect/connect-dialog/connect-method/connect-method.component.ts b/src/app/elements/connect/connect-dialog/connect-method/connect-method.component.ts index c32e8c35..88edd681 100644 --- a/src/app/elements/connect/connect-dialog/connect-method/connect-method.component.ts +++ b/src/app/elements/connect/connect-dialog/connect-method/connect-method.component.ts @@ -99,7 +99,7 @@ export class ElementConnectMethodComponent implements OnInit { if (this.account && !this.account.has_secret) { const aliases = ['@USER', '@INPUT']; // 同名账号、手动输入可以下载RDP文件 - if (!aliases.includes(this.account.alias) || (!this.manualAuthInfo.secret || !this.manualAuthInfo.username)) { + if (!aliases.includes(this.account.alias) || (!this.manualAuthInfo.username)) { return false; } } diff --git a/src/app/elements/content/content.component.ts b/src/app/elements/content/content.component.ts index fa4cc0dd..40dde9b9 100644 --- a/src/app/elements/content/content.component.ts +++ b/src/app/elements/content/content.component.ts @@ -93,32 +93,13 @@ export class ElementContentComponent implements OnInit, OnDestroy { handleKeyDownTabChange() { this.keyboardSubscription = fromEvent(window, 'keydown').subscribe((event: any) => { if (event.altKey && (event.key === 'ArrowRight' || event.key === 'ArrowLeft') && this.viewList.length > 1) { - window.onblur = () => { - setTimeout(() => window.focus(), 100); - }; - let nextViewId: any = 0; - let nextActiveView = null; - const viewIds = this.viewSrv.viewIds; - const currentViewIndex = viewIds.findIndex(i => i === this.viewSrv.currentView.id); + let key = ''; if (event.key === 'ArrowRight') { - if (currentViewIndex === viewIds.length - 1 && currentViewIndex !== 0) { - nextActiveView = this.viewList.find(i => i.id === viewIds[0]); - } else { - nextViewId = viewIds[currentViewIndex + 1]; - nextActiveView = this.viewList.find(i => i.id === nextViewId); - } - } - if (event.key === 'ArrowLeft') { - if (currentViewIndex === 0) { - nextActiveView = this.viewList.find(i => i.id === viewIds[viewIds.length - 1]); - } else { - nextViewId = viewIds[currentViewIndex - 1]; - nextActiveView = this.viewList.find(i => i.id === nextViewId); - } - } - if (nextActiveView) { - this.setViewActive(nextActiveView); + key = 'alt+right'; + } else if (event.key === 'ArrowLeft') { + key = 'alt+left'; } + this.viewSrv.keyboardSwitchTab(key); } }); } @@ -198,7 +179,6 @@ export class ElementContentComponent implements OnInit, OnDestroy { if (list[i].protocol !== 'ssh' || list[i].connected !== true) { continue; } - list[i].termComp.sendCommand({'data': cmd}); const subViews = list[i].subViews; if (subViews.length > 1) { for (let j = 0; j < subViews.length; j++) { @@ -207,6 +187,8 @@ export class ElementContentComponent implements OnInit, OnDestroy { } subViews[j].termComp.sendCommand({'data': cmd}); } + } else { + list[i].termComp.sendCommand({'data': cmd}); } } @@ -239,7 +221,9 @@ export class ElementContentComponent implements OnInit, OnDestroy { title: 'Reconnect', icon: 'fa-refresh', callback: () => { - this.viewList[this.rIdx].termComp.reconnect(); + const viewId = this.viewIds[this.rIdx]; + const currentView = this.viewList.find(i => i.id === viewId); + currentView.termComp.reconnect(); } }, { @@ -258,7 +242,9 @@ export class ElementContentComponent implements OnInit, OnDestroy { title: 'Close Current Tab', icon: 'fa-close', callback: () => { - this.closeView(this.viewList[this.rIdx]); + const viewId = this.viewIds[this.rIdx]; + const currentView = this.viewList.find(i => i.id === viewId); + this.closeView(currentView); } }, { diff --git a/src/app/elements/iframe/iframe.component.ts b/src/app/elements/iframe/iframe.component.ts index 35d2d5dd..4a1d0e56 100644 --- a/src/app/elements/iframe/iframe.component.ts +++ b/src/app/elements/iframe/iframe.component.ts @@ -1,9 +1,8 @@ import {AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild} from '@angular/core'; import {View} from '@app/model'; -import {ConnectTokenService, HttpService, I18nService, LogService} from '@app/services'; +import {ConnectTokenService, HttpService, I18nService, LogService, ViewService} from '@app/services'; import {MatDialog} from '@angular/material'; import {environment} from '@src/environments/environment'; - @Component({ selector: 'elements-iframe', templateUrl: './iframe.component.html', @@ -28,6 +27,7 @@ export class ElementIframeComponent implements OnInit, AfterViewInit, OnDestroy private _connectTokenSvc: ConnectTokenService, private _http: HttpService, private _dialog: MatDialog, + public viewSrv: ViewService, ) { } @@ -63,6 +63,12 @@ export class ElementIframeComponent implements OnInit, AfterViewInit, OnDestroy case 'CLICK': document.body.click(); break; + case 'KEYEVENT': + window.focus(); + setTimeout(() => { + this.viewSrv.keyboardSwitchTab(msg.data); + }, 200); + break; } }.bind(this); } diff --git a/src/app/pages/main/main.component.ts b/src/app/pages/main/main.component.ts index 4b3d1d9c..42e999dc 100644 --- a/src/app/pages/main/main.component.ts +++ b/src/app/pages/main/main.component.ts @@ -1,5 +1,5 @@ import {Component, ElementRef, HostListener, OnInit, ViewChild} from '@angular/core'; -import {DataStore, User} from '@app/globals'; +import {DataStore, DEFAULT_ORG_ID, SYSTEM_ORG_ID, User} from '@app/globals'; import {IOutputData, SplitComponent} from 'angular-split'; import {LogService, SettingService, ViewService} from '@app/services'; import * as _ from 'lodash'; @@ -19,6 +19,7 @@ export class PageMainComponent implements OnInit { showIframeHider = false; showSubMenu: any = false; menus: Array; + isDirectNavigation: boolean; settingLayoutSize = { leftWidth: 20, rightWidth: 80 @@ -87,6 +88,9 @@ export class PageMainComponent implements OnInit { ngOnInit(): void { console.log('main init'); + this._settingSvc.isDirectNavigation$.subscribe((state) => { + this.isDirectNavigation = state; + }); this.menus = [ { name: 'assets', @@ -140,9 +144,10 @@ export class PageMainComponent implements OnInit { @HostListener('window:beforeunload', ['$event']) unloadNotification($event: any) { - if (!environment.production) { + if (!environment.production || this.isDirectNavigation) { return; } + const notInIframe = window.self === window.top; const notInReplay = location.pathname.indexOf('/luna/replay') === -1; const returnValue = !(notInIframe && notInReplay); diff --git a/src/app/services/app.ts b/src/app/services/app.ts index 8ba87f31..350da412 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -11,6 +11,7 @@ import * as CryptoJS from 'crypto-js'; import {getCookie, setCookie} from '@app/utils/common'; import {OrganizationService} from './organization'; import {I18nService} from '@app/services/i18n'; +import {config} from 'rxjs'; declare function unescape(s: string): string; @@ -103,6 +104,14 @@ export class AppService { }, second * 1000); } + isRenewalExpired(currentTimeStamp, sessionExpireTimestamp, renewalTime: number = 60 * 2) { + if (!sessionExpireTimestamp) { + return false; + } + const timeDifferenceInSeconds = currentTimeStamp - parseInt(sessionExpireTimestamp, 10); + return timeDifferenceInSeconds > renewalTime; + } + checkLogin() { this._logger.debug('Check user auth'); if (!DataStore.Path) { @@ -122,13 +131,27 @@ export class AppService { const token = this.getQueryString('token'); // Determine whether the user has logged in const sessionExpire = getCookie('jms_session_expire'); + const renewalTime = 120; if (!sessionExpire && !token) { - setCookie('jms_session_expire', 'close', 120); + setCookie('jms_session_expire', 'close', renewalTime); gotoLogin(); return; } else if (sessionExpire === 'close') { - setInterval(() => { - setCookie('jms_session_expire', sessionExpire, 120); + const intervalId = setInterval(() => { + const currentTimeStamp = Math.floor(new Date().getTime() / 1000); + const sessionExpireTimestamp = getCookie('jms_session_expire_timestamp'); + if (!this.isRenewalExpired(currentTimeStamp, sessionExpireTimestamp, renewalTime)) { + setCookie('jms_session_expire', sessionExpire, renewalTime); + } + if (currentTimeStamp >= parseInt(sessionExpireTimestamp, 10)) { + confirm(this._i18n.instant('LoginExpireMsg')); + if (!this.newLoginHasOpen) { + this._settingSvc.isDirectNavigation$.next(true); + window.location.href = document.location.origin + '/core/auth/logout/'; + this.newLoginHasOpen = true; + } + clearInterval(intervalId); + } }, 10 * 1000); } diff --git a/src/app/services/setting.ts b/src/app/services/setting.ts index 461df6de..de89cda0 100644 --- a/src/app/services/setting.ts +++ b/src/app/services/setting.ts @@ -15,6 +15,7 @@ export class SettingService { public isLoadTreeAsync$ = new BehaviorSubject(true); public appletConnectMethod$ = new BehaviorSubject(''); public keyboardLayout$ = new BehaviorSubject(''); + public isDirectNavigation$ = new BehaviorSubject(false); constructor( diff --git a/src/app/services/view.ts b/src/app/services/view.ts index 92491efb..fe2e8bac 100644 --- a/src/app/services/view.ts +++ b/src/app/services/view.ts @@ -1,7 +1,6 @@ import {Injectable} from '@angular/core'; import {View} from '@app/model'; - @Injectable() export class ViewService { viewList: Array = []; @@ -72,4 +71,30 @@ export class ViewService { break; } } + + keyboardSwitchTab(key) { + let nextViewId: any = 0; + let nextActiveView = null; + const viewIds = this.viewIds; + const currentViewIndex = viewIds.findIndex(i => i === this.currentView.id); + if (key === 'alt+right') { + if (currentViewIndex === viewIds.length - 1 && currentViewIndex !== 0) { + nextActiveView = this.viewList.find(i => i.id === viewIds[0]); + } else { + nextViewId = viewIds[currentViewIndex + 1]; + nextActiveView = this.viewList.find(i => i.id === nextViewId); + } + } + if (key === 'alt+left') { + if (currentViewIndex === 0) { + nextActiveView = this.viewList.find(i => i.id === viewIds[viewIds.length - 1]); + } else { + nextViewId = viewIds[currentViewIndex - 1]; + nextActiveView = this.viewList.find(i => i.id === nextViewId); + } + } + if (nextActiveView) { + this.activeView(nextActiveView); + } + } }