diff --git a/src/app/elements/connect/connect-dialog/select-account/select-account.component.ts b/src/app/elements/connect/connect-dialog/select-account/select-account.component.ts index 8d23781bf..cfb7a6a4c 100644 --- a/src/app/elements/connect/connect-dialog/select-account/select-account.component.ts +++ b/src/app/elements/connect/connect-dialog/select-account/select-account.component.ts @@ -57,7 +57,7 @@ export class ElementSelectAccountComponent implements OnInit, OnDestroy { private _cdRef: ChangeDetectorRef ) { this.usernamePlaceholder = this._i18n.instant('Username'); - this.rememberAuthDisabled = !this._settingSvc.globalSetting.SECURITY_LUNA_REMEMBER_AUTH; + this.rememberAuthDisabled = false; } get noSecretAccounts() { diff --git a/src/app/elements/content/content-tab/content-tab.component.scss b/src/app/elements/content/content-tab/content-tab.component.scss index b8241599b..8d803b7c4 100644 --- a/src/app/elements/content/content-tab/content-tab.component.scss +++ b/src/app/elements/content/content-tab/content-tab.component.scss @@ -75,9 +75,20 @@ li { .view_icon { margin-right: 5px; font-style: normal; + display: flex; + width: 14px; + height: 14px; + align-items: center; + background-repeat: no-repeat; + background-position: center; + background-size: 14px 14px; + vertical-align: middle; &::before { display: inline-block; + width: 16px; + line-height: 16px; + text-align: center; color: var(--el-icon-color); font-family: FontAwesome; } diff --git a/src/app/elements/content/content-tab/content-tab.component.ts b/src/app/elements/content/content-tab/content-tab.component.ts index 57d5cdb18..ea0660950 100644 --- a/src/app/elements/content/content-tab/content-tab.component.ts +++ b/src/app/elements/content/content-tab/content-tab.component.ts @@ -3,8 +3,10 @@ import { ElementRef, EventEmitter, Input, + OnChanges, OnInit, Output, + SimpleChanges, ViewChild } from '@angular/core'; import { View, ViewAction } from '@app/model'; @@ -15,20 +17,23 @@ import { View, ViewAction } from '@app/model'; templateUrl: 'content-tab.component.html', styleUrls: ['content-tab.component.scss'] }) -export class ElementContentTabComponent implements OnInit { +export class ElementContentTabComponent implements OnInit, OnChanges { @Input() view: View; @Output() onAction: EventEmitter = new EventEmitter(); @ViewChild('inputElement', { static: false }) inputElement: ElementRef; - public iconCls: string; + public iconCls: string[] = []; private shouldFocusInput = false; private clickTimeout: any; + private static faIconCache = new Map(); ngOnInit(): void { - if (!this.view.asset) { - this.iconCls = 'fa-linux'; - } else { - this.iconCls = 'fa-' + this.view.asset.type.value; + this.updateIconClasses(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.view) { + this.updateIconClasses(); } } @@ -70,4 +75,57 @@ export class ElementContentTabComponent implements OnInit { this.view.editable = false; }, 200); } + + private updateIconClasses(): void { + const iconKey = this.view?.asset?.type?.value || 'linux'; + const faClass = `fa-${iconKey}`; + const fallbackClass = this.getFallbackIconClass(iconKey); + const hasFontAwesome = this.hasFontAwesomeIcon(faClass); + + this.iconCls = hasFontAwesome ? ['fa', faClass] : [fallbackClass]; + } + + private getFallbackIconClass(iconKey: string): string { + const normalizedKey = iconKey || 'linux'; + const overrides: Record = { + general: 'switch_ico_docu', + website: 'website_ico_docu', + sqlserver: 'sqlserver_ico_docu', + postgresql: 'postgresql_ico_docu', + mysql: 'mysql_ico_docu', + mariadb: 'mariadb_ico_docu', + mongodb: 'mongodb_ico_docu', + webcloud: 'WebCloud_ico_docu', + web_cloud: 'WebCloud_ico_docu', + 'web-cloud': 'WebCloud_ico_docu' + }; + + return overrides[normalizedKey] || `${normalizedKey}_ico_docu`; + } + + private hasFontAwesomeIcon(iconClass: string): boolean { + const cached = ElementContentTabComponent.faIconCache.get(iconClass); + if (cached !== undefined) { + return cached; + } + + if (typeof document === 'undefined' || typeof window === 'undefined') { + return true; + } + + const el = document.createElement('i'); + el.className = `view_icon fa ${iconClass}`; + el.style.position = 'absolute'; + el.style.opacity = '0'; + el.style.pointerEvents = 'none'; + document.body.appendChild(el); + + const content = window.getComputedStyle(el, '::before').getPropertyValue('content'); + document.body.removeChild(el); + + const normalizedContent = (content || '').replace(/['"]/g, ''); + const hasContent = normalizedContent !== '' && normalizedContent !== 'none' && normalizedContent !== 'normal'; + ElementContentTabComponent.faIconCache.set(iconClass, hasContent); + return hasContent; + } } diff --git a/src/app/elements/content/content.component.html b/src/app/elements/content/content.component.html index a6d3890b7..d97fdd3f8 100644 --- a/src/app/elements/content/content.component.html +++ b/src/app/elements/content/content.component.html @@ -3,10 +3,12 @@ +
- - + +
+
+ + @if (viewList.length > 0) { + + }
) { moveItemInArray(this.viewIds, event.previousIndex, event.currentIndex); } + + handleFullscreen() { + const ele: any = document.getElementsByClassName('window active')[0]; + + if (!ele) return; + + const requestFullscreen = + ele.requestFullscreen || + ele.webkitRequestFullscreen || + ele.mozRequestFullScreen || + ele.msRequestFullscreen; + + if (!requestFullscreen) { + throw new Error('不支持全屏api'); + } + + requestFullscreen.call(ele); + } } diff --git a/src/app/elements/nav/setting/setting.component.html b/src/app/elements/nav/setting/setting.component.html index 2d2c6551a..b506ab337 100644 --- a/src/app/elements/nav/setting/setting.component.html +++ b/src/app/elements/nav/setting/setting.component.html @@ -109,6 +109,7 @@
+
diff --git a/src/app/services/app.ts b/src/app/services/app.ts index 899a80a26..568ad469c 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -30,6 +30,8 @@ export class AppService { private protocolPreferKey = 'ProtocolPreferLoginType'; private accountPreferKey = 'PreferAccount'; private protocolConnectTypesMap: object = {}; + private connectMethodsRequestSeq = 0; + private connectMethodsAppliedSeq = 0; private checkIntervalId: number; private newLoginHasOpen = false; // 避免多次打开新登录页 private checkSecond = 120; @@ -202,13 +204,21 @@ export class AppService { getConnectMethods(): Promise { const url = '/api/v1/terminal/components/connect-methods/'; + const requestSeq = ++this.connectMethodsRequestSeq; // 当用户先打开 luna 并进行操作时如果后在 lina 中去设置连接方式的 ACL 此时并不生效,因此修改为在 toPromise 后直接赋值 return this._http .get(url) .toPromise() .then(response => { + // 每次发起请求都自增 connectMethodsRequestSeq,记录当前请求序号 requestSeq + // 响应回来时,如果 requestSeq 小于已经应用过的 connectMethodsAppliedSeq,说明这是“过期响应”,直接丢弃 + // 只有最新的响应才会更新 protocolConnectTypesMap,并把 connectMethodsAppliedSeq 同步为最新 + if (requestSeq < this.connectMethodsAppliedSeq) { + return; + } this.protocolConnectTypesMap = response; + this.connectMethodsAppliedSeq = requestSeq; }) .catch(error => { this._logger.error('Get connect methods error:', error); @@ -258,6 +268,7 @@ export class AppService { connectOption, direct: connectData.direct }; + this.setAccountLocalAuth(asset, account, manualAuthInfo); this._localStorage.set(key, saveData); } @@ -346,7 +357,7 @@ export class AppService { newAuth.alias = account.alias; } - if (!auth.secret || !auth.rememberAuth || !this._settingSvc.globalSetting.SECURITY_LUNA_REMEMBER_AUTH) { + if (!auth.secret || !auth.rememberAuth) { newAuth.secret = ''; } else { newAuth.secret = this.encrypt(auth.secret);