diff --git a/src/app/elements/chat/chat.component.html b/src/app/elements/chat/chat.component.html index c52c3ebbe..32ec5f09b 100644 --- a/src/app/elements/chat/chat.component.html +++ b/src/app/elements/chat/chat.component.html @@ -25,7 +25,6 @@ - diff --git a/src/app/elements/chat/chat.component.ts b/src/app/elements/chat/chat.component.ts index ed892bd24..a3051d5ec 100644 --- a/src/app/elements/chat/chat.component.ts +++ b/src/app/elements/chat/chat.component.ts @@ -215,14 +215,7 @@ export class ElementChatComponent implements OnInit, OnDestroy, AfterViewInit { handleShowDrawer() { if (this.currentView.iframeElement) { - switch (this.currentView.connectMethod.component) { - case 'koko': - this._drawerStateService.sendComponentMessage({ name: 'OPEN_SETTING' }); - break; - default: - this.currentView.iframeElement.postMessage({ name: 'OPEN' }, '*'); - break; - } + this.currentView.iframeElement.postMessage({ name: 'OPEN' }, '*'); } } diff --git a/src/app/elements/content/content-window/koko/koko.component.ts b/src/app/elements/content/content-window/koko/koko.component.ts index 085af980c..3cda9e69d 100644 --- a/src/app/elements/content/content-window/koko/koko.component.ts +++ b/src/app/elements/content/content-window/koko/koko.component.ts @@ -75,7 +75,7 @@ export class ElementConnectorKokoComponent implements OnInit { } iframeWindow.postMessage( - { name: 'CREATE_FILE_CONNECT_TOKEN', SFTP_Token: newConnectToken.id }, + { name: 'GET_FILE_CONNECT_TOKEN', token: newConnectToken.id }, '*' ); } diff --git a/src/app/elements/drawer/drawer.component.html b/src/app/elements/drawer/drawer.component.html deleted file mode 100644 index 116623a85..000000000 --- a/src/app/elements/drawer/drawer.component.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
- -
- -
- - - - - - - - - - - -
-
diff --git a/src/app/elements/drawer/drawer.component.scss b/src/app/elements/drawer/drawer.component.scss deleted file mode 100644 index fd9586239..000000000 --- a/src/app/elements/drawer/drawer.component.scss +++ /dev/null @@ -1,116 +0,0 @@ -::ng-deep .elements-drawer-wrapper { - background-color: var(--el-drawer-bg-color); - width: 600px; - height: calc(100vh - 60px); - overflow: auto; - position: absolute; - top: 60px; - right: 0; - z-index: 999; - padding: 10px 30px; - - transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; - - &.drawer-hidden { - transform: translateX(100%); - opacity: 0; - pointer-events: none; - } - - &.drawer-visible { - transform: translateX(0); - opacity: 1; - pointer-events: auto; - } - - .close-button { - position: absolute; - top: 20px; - right: 20px; - z-index: 1000; - - svg { - color: var(--el-text-color-light); - } - } - - .ant-tabs { - .ant-tabs-nav .ant-tabs-nav-list { - .ant-tabs-tab { - justify-content: center; - color: var(--el-text-color-light); - - &.ant-tabs-tab-disabled { - color: gray; - - .ant-tabs-tab-btn { - cursor: not-allowed !important; - } - } - } - } - - .ant-tabs-content-holder { - .ant-select { - .ant-select-selector { - background-color: var(--el-drawer-collapse-bg-color); - border: 1px solid var(--el-divider-border-color); - border-radius: 6px; - color: var(--el-text-color-light); - - &:hover { - border-color: var(--el-form-hover-border-color); - background-color: var(--el-drawer-bg-color); - } - } - - .ant-select-arrow { - color: var(--el-text-color-light); - } - } - } - } - - .ant-card { - margin-top: 12px; - background-color: var(--el-drawer-bg-color); - border-color: var(--el-divider-border-color); - - .ant-card-head-title { - color: var(--el-text-color-light); - } - - .ant-card-body { - color: var(--el-text-color-light); - } - } - - .ant-input-group-wrapper { - textarea, - .ant-input-group-addon { - background-color: var(--el-drawer-bg-color); - } - } - - .ant-drawer-body { - padding: 0 24px; - } - - .ant-drawer-wrapper-body { - background-color: var(--el-drawer-bg-color); - - .ant-drawer-header { - padding: 16px 0 0 24px; - border-bottom: unset; - - .ant-drawer-header-title { - justify-content: flex-end; - - svg { - color: var(--el-text-color-light); - } - } - } - - } -} diff --git a/src/app/elements/drawer/drawer.component.ts b/src/app/elements/drawer/drawer.component.ts deleted file mode 100644 index 357a601ec..000000000 --- a/src/app/elements/drawer/drawer.component.ts +++ /dev/null @@ -1,413 +0,0 @@ -import _ from 'lodash-es'; - -import { View } from '@app/model'; -import { Subscription } from 'rxjs'; -import { IframeCommunicationService, DrawerStateService } from '@app/services'; -import { Component, OnInit, Input, effect, signal, OnDestroy, ViewChild } from '@angular/core'; -import { ElementFileManagerComponent } from './filemanager/filemanager.component'; - -import type { OnlineUsers } from '@app/model'; - -interface DrawerViewState { - onLineUsers: OnlineUsers[]; - isVisible: boolean; - isChatOpen: boolean; - isDrawerOpen: boolean; - isSettingOpen: boolean; - currentTabIndex: number; - terminalContent: {} | null; - disabledFileManager: boolean; - disabledShortcutKeys: boolean; -} - -@Component({ - standalone: false, - selector: 'elements-drawer', - templateUrl: 'drawer.component.html', - styleUrls: ['drawer.component.scss'] -}) -export class ElementDrawerComponent implements OnInit, OnDestroy { - @Input() view: View; - @ViewChild('fileManagerComponent') fileManagerComponent: ElementFileManagerComponent; - - readonly showChat = signal(false); - readonly showDrawer = signal(false); - readonly showSetting = signal(false); - readonly hasConnected = signal(false); - readonly visible = signal(false); - - drawerInited = false; - disabledFileManager = false; - disabledShortcutKeys = false; - - currentTabIndex = 0; - currentViewId: string | null = null; - onLineUsers: OnlineUsers[] = []; - chatIframeURL = ''; - terminalContent: {} | null = null; - - private readonly DISABLED_CATEGORIES = ['database', 'web']; - private readonly DISABLED_ASSET_TYPES = ['windows', 'website']; - private readonly DISABLED_PROTOCOLS = ['telnet']; - - private readonly drawerStateMap = new Map(); - - private isFileManagerDisabled(view: View): boolean { - if (!view || !view.asset) { - return false; - } - - // 检查资产类别是否被禁用 - if (view.asset.category && this.DISABLED_CATEGORIES.includes(view.asset.category.value)) { - return true; - } - - // Windows资产的特殊处理 - if (view.asset.type && view.asset.type.value === 'windows') { - // Windows下的SSH不禁用文件管理器 - if (view.protocol === 'ssh') { - return false; - } - // Windows下的其他协议(如RDP)禁用文件管理器 - return true; - } - - // 检查其他资产类型是否被禁用 - if (view.asset.type && view.asset.type.value === 'website') { - return true; - } - - // 检查协议是否被禁用(文件管理器需要禁用telnet) - if (view.protocol && this.DISABLED_PROTOCOLS.includes(view.protocol)) { - return true; - } - - return false; - } - - private isShortcutKeysDisabled(view: View): boolean { - if (!view || !view.asset) { - return false; - } - - // 检查资产类别是否被禁用 - if (view.asset.category && this.DISABLED_CATEGORIES.includes(view.asset.category.value)) { - return true; - } - - // Windows资产的特殊处理 - if (view.asset.type && view.asset.type.value === 'windows') { - // Windows下的SSH应该启用快捷键 - if (view.protocol === 'ssh') { - return false; - } - // Windows下的其他协议(如RDP)禁用快捷键 - return true; - } - - // 检查其他资产类型是否被禁用 - if (view.asset.type && view.asset.type.value === 'website') { - return true; - } - - return false; - } - - private iframeMessageSubscription: Subscription; - private componentsMessageSubscription: Subscription; - - constructor( - private readonly _drawerStateService: DrawerStateService, - private readonly _iframeSvc: IframeCommunicationService - ) { - this.initializeComponent(); - } - - ngOnInit(): void { - this.drawerInited = true; - this.resetDrawerState(); - } - - ngOnDestroy(): void { - this.iframeMessageSubscription?.unsubscribe(); - this.componentsMessageSubscription?.unsubscribe(); - } - - private initializeComponent(): void { - this.setupSideEffects(); - this.subscribeMessages(); - } - - private resetDrawerState(): void { - this.showDrawer.set(false); - this.showSetting.set(false); - this.showChat.set(false); - } - - private setupSideEffects(): void { - effect(() => { - if (this.visible()) { - this.checkConnectionStatus(); - } - - if (this.hasConnected()) { - if (this.view) { - this.disabledFileManager = this.isFileManagerDisabled(this.view); - this.disabledShortcutKeys = this.isShortcutKeysDisabled(this.view); - - // 如果文件管理器被禁用,默认切换到第二个tab(会话共享) - if (this.disabledFileManager && this.currentTabIndex === 0) { - this.currentTabIndex = 1; - } - } - } - }); - } - - private checkConnectionStatus(): void { - const isConnected = Boolean(this.view?.iframeElement); - - if (isConnected) { - this.hasConnected.set(true); - - return; - } - } - - private subscribeMessages(): void { - this.iframeMessageSubscription = this._iframeSvc.message$.subscribe(message => { - this.handleIframeMessage(message); - }); - this.componentsMessageSubscription = this._drawerStateService.message$.subscribe(message => { - this.handleIframeMessage(message); - }); - } - - private handleIframeMessage(message: any): void { - const messageHandlers = { - SSH_CLOSE: this.handleSshClose.bind(this), - OPEN_CHAT: this.handleOpenChat.bind(this), - OPEN_SETTING: this.handleOpenSetting.bind(this), - - DRAWER_RECONNECT: this.handleDrawerReconnect.bind(this), - TAB_VIEW_CHANGE: this.handleTabViewChange.bind(this), - ALL_VIEWS_CLOSED: this.handleAllViewsClosed.bind(this), - TERMINAL_CONTENT_RESPONSE: this.handleTerminalContentResponse.bind(this) - }; - - const handler = messageHandlers[message.name]; - if (handler) { - // 对于OPEN_SETTING消息,需要传递完整的data对象 - if (message.name === 'OPEN_SETTING') { - handler(message.data); - } else { - handler(message.data); - } - } - } - - private handleDrawerReconnect(): void { - // 通知 filemanager 重新创建 - this.fileManagerComponent.reconnect(); - - // 清理当前 viewId 下对应的 onlineUsers - this.onLineUsers = []; - this.saveCurrentViewState(); - } - - private handleSshClose(currentView: View): void { - const currentViewId = currentView.id; - - if (this.drawerStateMap.has(currentViewId)) { - if (this.currentViewId === currentViewId) { - this.onLineUsers = []; - this.visible.set(false); - } - - if (this.fileManagerComponent) { - this.fileManagerComponent.destroyIframeByViewId(currentViewId); - } - - this.drawerStateMap.delete(currentViewId); - - console.log(`SSH 连接已关闭,视图 ${currentViewId} 的状态已清理`); - } - - if (this.currentViewId === currentViewId) { - this.saveCurrentViewState(); - } - } - - private handleAllViewsClosed(): void { - this.resetDrawerState(); - this.visible.set(false); - } - - private handleTabViewChange(viewId: string): void { - if (viewId === this.currentViewId) return; - - // 保存当前视图的状态(包括当前的 tab 索引) - this.saveCurrentViewState(); - - // 恢复新视图的状态(包括它最后激活时的 tab 索引) - this.restoreViewState(viewId); - - this.currentViewId = viewId; - } - - private handleOpenSetting(data?: any): void { - this.drawerInited = true; - this.showSetting.set(true); - - // 启用 filemanager 的逻辑执行 - if (this.fileManagerComponent) { - // 如果有direct模式的数据,传递给filemanager - if (data && data.direct && data.fileManagerToken) { - this.fileManagerComponent.enableFileManagerLogic(data.fileManagerToken, true); - } else { - this.fileManagerComponent.enableFileManagerLogic(); - } - } - - const viewId = this.view?.id || null; - - if (viewId) { - if (this.currentViewId && this.currentViewId !== viewId) { - this.saveCurrentViewState(); - } - - this.currentViewId = viewId; - this.restoreViewState(viewId); - } else { - this.currentTabIndex = 0; - } - - this.visible.set(true); - } - - private handleOpenChat(chatIframeURL: string): void { - this.chatIframeURL = chatIframeURL; - this.showChat.set(true); - this.showDrawer.set(true); - this.saveCurrentViewState(); - } - - private handleTerminalContentResponse(data: string): void { - this.terminalContent = data; - this.saveCurrentViewState(); - } - - private restoreViewState(viewId: string): void { - if (this.drawerStateMap.has(viewId)) { - const savedState = this.drawerStateMap.get(viewId)!; - - this.visible.set(savedState.isVisible); - this.showChat.set(savedState.isChatOpen); - this.showDrawer.set(savedState.isDrawerOpen); - this.showSetting.set(savedState.isSettingOpen); - this.onLineUsers = [...savedState.onLineUsers]; - this.terminalContent = savedState.terminalContent || null; - this.currentTabIndex = savedState.currentTabIndex; - this.disabledFileManager = savedState.disabledFileManager; - this.disabledShortcutKeys = savedState.disabledShortcutKeys; - } else { - this.initializeNewTabState(viewId); - } - } - - private createDefaultViewState(): DrawerViewState { - return { - isVisible: false, - isChatOpen: false, - isDrawerOpen: false, - isSettingOpen: false, - currentTabIndex: 0, - terminalContent: null, - onLineUsers: [], - disabledFileManager: false, - disabledShortcutKeys: false - }; - } - - close(): void { - this.visible.set(false); - } - - onTabChange(event: { index: number; tab: any }): void { - // 在切换 tab 之前保存当前状态 - this.saveCurrentViewState(); - - this.currentTabIndex = event.index; - - if (event.index === 0) { - this.currentViewId = this.view?.id || null; - } - - // 切换后再次保存状态 - this.saveCurrentViewState(); - } - - saveCurrentViewState(): void { - if (this.currentViewId) { - this.drawerStateMap.set(this.currentViewId, { - onLineUsers: [...this.onLineUsers], - isDrawerOpen: this.showDrawer(), - isSettingOpen: this.showSetting(), - isChatOpen: this.showChat(), - terminalContent: this.terminalContent || null, - currentTabIndex: this.currentTabIndex, - isVisible: this.visible(), - disabledFileManager: this.disabledFileManager, - disabledShortcutKeys: this.disabledShortcutKeys - }); - } - } - - initializeNewTabState(viewId: string): void { - const defaultState = this.createDefaultViewState(); - - // 根据资产类型分别设置禁用状态 - if (this.view) { - defaultState.disabledFileManager = this.isFileManagerDisabled(this.view); - defaultState.disabledShortcutKeys = this.isShortcutKeysDisabled(this.view); - - // 如果文件管理器被禁用,默认切换到第二个tab - if (defaultState.disabledFileManager) { - defaultState.currentTabIndex = 1; - } - } - - this.onLineUsers = []; - this.currentTabIndex = defaultState.currentTabIndex; - this.showDrawer.set(false); - this.showSetting.set(false); - this.showChat.set(false); - this.visible.set(false); - this.disabledFileManager = defaultState.disabledFileManager; - this.disabledShortcutKeys = defaultState.disabledShortcutKeys; - - this.drawerStateMap.set(viewId, defaultState); - } - - onLineUsersAdd(data: OnlineUsers): void { - // 只有在有 currentViewId 的情况下才添加用户 - if (this.currentViewId) { - this.onLineUsers = [...this.onLineUsers, data]; - this.saveCurrentViewState(); - } - } - - onDeleteShareUser(item: OnlineUsers): void { - this._iframeSvc.sendMessage({ - name: 'SHARE_USER_REMOVE', - data: item - }); - - if (this.currentViewId && this.view?.id === this.currentViewId) { - this.onLineUsers = this.onLineUsers.filter( - user => !(user.terminal_id === item.terminal_id && user.primary === item.primary) - ); - this.saveCurrentViewState(); - } - } -} diff --git a/src/app/elements/drawer/filemanager/filemanager.component.html b/src/app/elements/drawer/filemanager/filemanager.component.html deleted file mode 100644 index 165f7f113..000000000 --- a/src/app/elements/drawer/filemanager/filemanager.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
-
- -
{{ 'FileManagerConnectionFailed' | translate }}
-
{{ 'TokenVerificationFailedLeaseReconnect' | translate }}
-
- -
- -
-
- -
-
- -
-
-
diff --git a/src/app/elements/drawer/filemanager/filemanager.component.scss b/src/app/elements/drawer/filemanager/filemanager.component.scss deleted file mode 100644 index 7125132db..000000000 --- a/src/app/elements/drawer/filemanager/filemanager.component.scss +++ /dev/null @@ -1,63 +0,0 @@ -.file-manager-empty.ant-empty { - color: var(--el-text-color-light); -} - -.loading-container { - width: 100%; - height: calc(100vh - 170px); - display: flex; - align-items: center; - justify-content: center; - - .loading-content { - text-align: center; - } -} - -.reconnect-container { - width: 100%; - height: calc(100vh - 170px); - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - - .reconnect-content { - text-align: center; - color: #666; - margin-bottom: 20px; - - .disconnect-icon { - font-size: 48px; - margin-bottom: 16px; - color: #ff4d4f; - } - - .error-title { - font-size: 16px; - margin-bottom: 8px; - font-weight: 500; - } - - .error-description { - font-size: 14px; - color: #999; - } - } - - .reconnect-button { - width: 120px; - height: 36px; - - .reload-icon { - margin-right: 6px; - } - } -} - -// iframe容器样式 -.iframe-container { - width: 100%; - height: calc(100vh - 170px); - position: relative; -} diff --git a/src/app/elements/drawer/filemanager/filemanager.component.ts b/src/app/elements/drawer/filemanager/filemanager.component.ts deleted file mode 100644 index e76eed7c0..000000000 --- a/src/app/elements/drawer/filemanager/filemanager.component.ts +++ /dev/null @@ -1,501 +0,0 @@ -import { View } from '@app/model'; -import { ConnectTokenService } from '@app/services'; -import { - Component, - OnDestroy, - OnInit, - AfterViewInit, - input, - ViewChild, - ElementRef, - Renderer2, - effect -} from '@angular/core'; -import { DrawerStateService } from '@app/services'; -import { Subscription } from 'rxjs'; - -@Component({ - standalone: false, - selector: 'elements-filemanager', - templateUrl: 'filemanager.component.html', - styleUrls: ['filemanager.component.scss'] -}) -export class ElementFileManagerComponent implements OnInit, AfterViewInit, OnDestroy { - @ViewChild('iframeContainer') iframeContainer: ElementRef; - - view = input(null); - currentViewId = input(''); - - private viewInitialized = false; - private viewIdOpenSettingStatus = new Map(); - private iframes = new Map(); - private currentDisplayedViewId: string | null = null; - private directTokens = new Map(); - - private readonly DISABLED_CATEGORIES = ['database', 'web']; - private readonly DISABLED_ASSET_TYPES = ['windows', 'website']; - private readonly DISABLED_PROTOCOLS = ['telnet']; - - private iframeMessageSubscription: Subscription; - private drawerMessageSubscription: Subscription; - private windowMessageListener: (event: MessageEvent) => void; - - private viewReconnectStates = new Map< - string, - { - showReconnectButton: boolean; - reconnectLoading: boolean; - failed: boolean; - loading: boolean; - hasValidIframe: boolean; - } - >(); - - // 计算属性:当前 viewId 的重连状态 - get showReconnectButton(): boolean { - const currentViewId = this.currentViewId(); - return this.viewReconnectStates.get(currentViewId)?.showReconnectButton || false; - } - - get reconnectLoading(): boolean { - const currentViewId = this.currentViewId(); - return this.viewReconnectStates.get(currentViewId)?.reconnectLoading || false; - } - - get showLoadingState(): boolean { - const currentViewId = this.currentViewId(); - const state = this.viewReconnectStates.get(currentViewId); - return state?.loading || false; - } - - get shouldShowIframeContainer(): boolean { - const currentViewId = this.currentViewId(); - const state = this.viewReconnectStates.get(currentViewId); - // 只有当前viewId有有效iframe且不在重连状态时才显示iframe容器 - return !this.showReconnectButton && !this.showLoadingState && (state?.hasValidIframe || false); - } - - private isDisabledCategory(): boolean { - const view = this.view(); - if (!view || !view.asset) { - return false; - } - - // 检查资产类别是否被禁用 - if (view.asset.category && this.DISABLED_CATEGORIES.includes(view.asset.category.value)) { - return true; - } - - // Windows资产的特殊处理 - if (view.asset.type && view.asset.type.value === 'windows') { - // Windows下的SSH不禁用文件管理器 - if (view.protocol === 'ssh') { - return false; - } - // Windows下的其他协议(如RDP)禁用文件管理器 - return true; - } - - // 检查其他资产类型是否被禁用 - if (view.asset.type && view.asset.type.value === 'website') { - return true; - } - - // 检查协议是否被禁用(文件管理器需要禁用telnet) - if (view.protocol && this.DISABLED_PROTOCOLS.includes(view.protocol)) { - return true; - } - - return false; - } - - private setReconnectState( - viewId: string, - state: Partial<{ - showReconnectButton: boolean; - reconnectLoading: boolean; - failed: boolean; - loading: boolean; - hasValidIframe: boolean; - }> - ) { - const currentState = this.viewReconnectStates.get(viewId) || { - showReconnectButton: false, - reconnectLoading: false, - failed: false, - loading: false, - hasValidIframe: false - }; - this.viewReconnectStates.set(viewId, { ...currentState, ...state }); - } - - private getReconnectState(viewId: string) { - return ( - this.viewReconnectStates.get(viewId) || { - showReconnectButton: false, - reconnectLoading: false, - failed: false, - loading: false, - hasValidIframe: false - } - ); - } - - constructor( - private readonly renderer: Renderer2, - private readonly _connectTokenSvc: ConnectTokenService, - private readonly _drawerStateService: DrawerStateService - ) { - effect(() => { - const currentViewId = this.currentViewId(); - const view = this.view(); - - if (!this.viewInitialized || !currentViewId || !view) { - return; - } - - // 检查是否为禁用的资产类型 - if (this.isDisabledCategory()) { - return; - } - - // 总是先切换视图显示状态(基于当前状态决定显示什么) - this.switchToView(currentViewId); - - // 如果已经有 iframe,无需重新创建 - if (this.iframes.has(currentViewId)) { - return; - } - - // 检查当前 viewId 的状态 - const currentState = this.getReconnectState(currentViewId); - - // 如果已经处于失败状态,不要自动重试,让用户手动点击重连 - if (currentState.failed && currentState.showReconnectButton) { - return; - } - - // 如果没有 iframe 且已经收到 OpenSetting 信号,则创建 - const hasReceivedOpenSetting = this.viewIdOpenSettingStatus.get(currentViewId) || false; - if (hasReceivedOpenSetting) { - // 设置loading状态 - this.setReconnectState(currentViewId, { loading: true }); - - this.createNewIframe(currentViewId, view) - .then(() => { - setTimeout(() => { - this.switchToView(currentViewId); - }, 100); - }) - .catch(error => { - console.error('创建 iframe 失败:', error); - // 失败时,显示重连按钮的逻辑已经在 createNewIframe 中处理 - }); - } else { - console.log( - 'Waiting for OpenSetting signal before creating iframe for viewId:', - currentViewId - ); - } - }); - } - - ngOnInit(): void { - window.addEventListener('message', (event: MessageEvent) => { - if (event.data && event.data.name === 'FILE_MANAGE_EXPIRED') { - this.handleFileManageExpired(event.data.data || {}); - } - }); - - this.drawerMessageSubscription = this._drawerStateService.message$.subscribe(message => { - if (message.name === 'CHANGE_MAIN_THEME') { - this.iframes.forEach(iframe => { - iframe.contentWindow.postMessage( - { - name: 'CHANGE_MAIN_THEME', - data: message.data - }, - '*' - ); - }); - } - }); - } - - ngAfterViewInit(): void { - this.viewInitialized = true; - } - - ngOnDestroy(): void { - this.iframes.clear(); - this.viewIdOpenSettingStatus.clear(); - this.viewReconnectStates.clear(); - this.directTokens.clear(); - this.iframeMessageSubscription?.unsubscribe(); - this.drawerMessageSubscription?.unsubscribe(); - } - - private handleFileManageExpired(data: any): void { - // 获取当前 viewId,如果消息中有特定的 viewId 则使用消息中的,否则使用当前的 - const targetViewId = data?.viewId || this.currentViewId(); - - if (targetViewId) { - // 清理失败的 iframe - this.cleanupFailedView(targetViewId); - - // 设置重连状态,显示重连按钮 - this.setReconnectState(targetViewId, { - showReconnectButton: true, - failed: true, - reconnectLoading: false, - loading: false, - hasValidIframe: false - }); - } else { - console.warn('无法确定 targetViewId,无法处理 FILE_MANAGE_EXPIRED 消息'); - } - } - - public enableFileManagerLogic(directToken?: string, isDirect?: boolean): void { - const currentViewId = this.currentViewId(); - if (!currentViewId) { - return; - } - - if (this.isDisabledCategory()) { - return; - } - - // 为当前 viewId 标记已收到 OpenSetting 信号 - this.viewIdOpenSettingStatus.set(currentViewId, true); - - // 如果是direct模式且有token,存储token以供后续使用 - if (isDirect && directToken) { - this.directTokens.set(currentViewId, directToken); - } - - const view = this.view(); - - if (this.viewInitialized && currentViewId && view && !this.iframes.has(currentViewId)) { - const currentState = this.getReconnectState(currentViewId); - - // 如果已经处于失败状态,不要自动重试,让用户手动点击重连 - if (currentState.failed && currentState.showReconnectButton) { - return; - } - - this.setReconnectState(currentViewId, { loading: true }); - - this.createNewIframe(currentViewId, view) - .then(() => { - setTimeout(() => { - this.switchToView(currentViewId); - }, 100); - }) - .catch(error => { - console.error('首次创建 iframe 失败:', error); - }); - } - } - - public destroyIframeByViewId(viewId: string): void { - const iframe = this.iframes.get(viewId); - - if (iframe && this.iframeContainer?.nativeElement) { - this.renderer.removeChild(this.iframeContainer.nativeElement, iframe); - - this.iframes.delete(viewId); - // 同时清理对应的 OpenSetting 状态 - this.viewIdOpenSettingStatus.delete(viewId); - - if (this.currentDisplayedViewId === viewId) { - this.currentDisplayedViewId = null; - } - } - - // 清理重连状态和direct token - this.viewReconnectStates.delete(viewId); - this.directTokens.delete(viewId); - } - - // 添加重连方法 - public async reconnect(): Promise { - const currentViewId = this.currentViewId(); - const view = this.view(); - - if (!currentViewId || !view || this.reconnectLoading) { - return; - } - - // 检查是否为禁用的资产类型 - if (this.isDisabledCategory()) { - return; - } - - // 额外检查:确认 currentViewId 与 view.id 是否一致 - if (view.id !== currentViewId) { - console.warn('ViewId 不一致!', { - inputCurrentViewId: currentViewId, - viewObjectId: view.id - }); - } - - this.setReconnectState(currentViewId, { - reconnectLoading: true, - loading: true, - showReconnectButton: false - }); - - try { - const actualViewId = view.id; - - this.cleanupFailedView(actualViewId); - - await this.createNewIframe(actualViewId, view); - - this.setReconnectState(actualViewId, { - showReconnectButton: false, - reconnectLoading: false, - failed: false, - loading: false, - hasValidIframe: true - }); - - setTimeout(() => { - this.switchToView(actualViewId); - - // 通知抽屉组件文件管理器已重连,需要刷新状态 - // 发送 TAB_VIEW_CHANGE 消息来触发抽屉状态刷新 - this._drawerStateService.sendComponentMessage({ - name: 'TAB_VIEW_CHANGE', - data: actualViewId - }); - }, 100); - } catch (error) { - console.error('重连失败:', error); - this.setReconnectState(currentViewId, { - reconnectLoading: false, - loading: false, - showReconnectButton: true, - failed: true, - hasValidIframe: false - }); - } - } - - private cleanupFailedView(viewId: string): void { - const existingIframe = this.iframes.get(viewId); - - if (existingIframe && this.iframeContainer?.nativeElement) { - this.renderer.removeChild(this.iframeContainer.nativeElement, existingIframe); - this.iframes.delete(viewId); - } - - if (this.currentDisplayedViewId === viewId) { - this.currentDisplayedViewId = null; - } - } - - private async createNewIframe(viewId: string, view: View): Promise { - if (!this.iframeContainer?.nativeElement) { - return; - } - - if (this.isDisabledCategory()) { - return; - } - - try { - const url = await this.generateIframeURL(view); - - if ( - !url || - url.includes('token=undefined') || - url.includes('token=null') || - !url.includes('token=') - ) { - throw new Error('Invalid token or URL generated'); - } - - const iframe = this.renderer.createElement('iframe'); - this.renderer.setAttribute(iframe, 'src', url); - this.renderer.setAttribute(iframe, 'frameborder', '0'); - this.renderer.setAttribute(iframe, 'data-view-id', viewId); - this.renderer.setStyle(iframe, 'width', '100%'); - this.renderer.setStyle(iframe, 'height', 'calc(100vh - 170px)'); - this.renderer.setStyle(iframe, 'display', 'block'); - this.renderer.appendChild(this.iframeContainer.nativeElement, iframe); - - this.iframes.set(viewId, iframe); - - // 成功创建iframe后更新状态 - this.setReconnectState(viewId, { - hasValidIframe: true, - loading: false, - failed: false - }); - } catch (error) { - console.error('Failed to create iframe for viewId:', viewId, error); - - this.setReconnectState(viewId, { - showReconnectButton: true, - failed: true, - reconnectLoading: false, - loading: false, - hasValidIframe: false - }); - - throw error; - } - } - - private switchToView(viewId: string): void { - this.iframes.forEach(iframe => { - this.renderer.setStyle(iframe, 'display', 'none'); - }); - - const reconnectState = this.getReconnectState(viewId); - - // 如果当前viewId有有效的iframe且不在失败状态,则显示对应的iframe - if (reconnectState.hasValidIframe && !reconnectState.failed && !reconnectState.loading) { - const currentIframe = this.iframes.get(viewId); - if (currentIframe) { - this.renderer.setStyle(currentIframe, 'display', 'block'); - this.currentDisplayedViewId = viewId; - } - } else { - // 如果没有有效 iframe 或处于失败 loading 状态,不显示任何 iframe - this.currentDisplayedViewId = null; - } - } - - private async generateIframeURL(view: View): Promise { - try { - const currentViewId = this.currentViewId(); - const directToken = this.directTokens.get(currentViewId); - - let tokenId: string; - - // 如果是direct模式且有直接 token,使用直接 token - if (directToken) { - tokenId = directToken; - } else { - const oldToken = view.connectToken; - const newToken = await this._connectTokenSvc.exchange(oldToken); - - if (!newToken || !newToken.id) { - throw new Error('Token exchange failed or returned invalid token'); - } - - tokenId = newToken.id; - } - - const url = `${view.smartEndpoint.getUrl()}/koko/sftp/?token=${tokenId}`; - - return url; - } catch (error) { - console.error('Failed to generate iframe URL:', error); - throw error; - } - } -} diff --git a/src/app/elements/drawer/session-share/session-share.component.html b/src/app/elements/drawer/session-share/session-share.component.html deleted file mode 100644 index 6b0e5081d..000000000 --- a/src/app/elements/drawer/session-share/session-share.component.html +++ /dev/null @@ -1,191 +0,0 @@ -
- @if (onLineUsers().length > 0) { - - - - } @else { - - } -
- - - - @if (showCreateShareLinkForm) { - {{ 'CreateLink' | translate }} - } @else { - {{ 'ShareLink' | translate }} - } - - - - @if (showCreateShareLinkForm) { -
- - {{ 'ExpiredTime' | translate }} - - - @for (option of shareExpiredOptions; track option.value) { - - - } - - - - - - {{ 'ActionPerm' | translate }} - - - @for (permission of shareUserPermissions; track permission.value) { - - } - - - - - - {{ 'ShareUser' | translate }} - - - @if (!loading) { - @for (o of searchedUserList; track o) { - - } - } @else { - - - Loading Data... - - } - - - -
- } - - @if (showLinkResult) { -
- - {{ 'ShareLink' | translate }} - - - - - - - {{ 'ShareCode' | translate }} - - - - -
- } -
- - - - - @if (showCreateShareLinkForm) { - - } - - @if (showLinkResult) { - - } - -
diff --git a/src/app/elements/drawer/session-share/session-share.component.scss b/src/app/elements/drawer/session-share/session-share.component.scss deleted file mode 100644 index 2adf8cd39..000000000 --- a/src/app/elements/drawer/session-share/session-share.component.scss +++ /dev/null @@ -1,69 +0,0 @@ -.share-user-list { - height: calc(100vh - 195px); - - .ant-tag { - margin: 0; - font-size: 11px; - padding: 0 6px; - height: 20px; - line-height: 18px; - border-radius: 4px; - } - - .ant-divider { - margin: 0 0 12px 0; - border-top-color: var(--el-divider-border-color); - - ::ng-deep .ant-divider-inner-text { - display: flex; - align-items: center; - gap: 8px; - padding: 0 12px; - font-size: 14px; - font-weight: 500; - color: var(--el-text-color-light); - } - - .ant-list { - .ant-list-item-meta { - align-items: center; - - .ant-list-item-meta-content { - .ant-list-item-meta-description, - .ant-list-item-meta-title { - margin-bottom: unset; - color: var(--el-text-color-light); - } - } - } - - .ant-list-item-action li { - color: var(--el-text-color-light); - - svg { - transition: color 0.3s ease; - - &:hover { - cursor: pointer; - color: red; - } - } - } - } - } -} - -.share-user-empty.ant-empty { - color: var(--el-text-color-light); -} - -::ng-deep .create-share-link-modal { - .ant-modal-content { - border-radius: 4px; - overflow: hidden; - - .ant-modal-header { - border-radius: 8px 8px 0 0; - } - } -} diff --git a/src/app/elements/drawer/session-share/session-share.component.ts b/src/app/elements/drawer/session-share/session-share.component.ts deleted file mode 100644 index 7d848c5cc..000000000 --- a/src/app/elements/drawer/session-share/session-share.component.ts +++ /dev/null @@ -1,300 +0,0 @@ -import _ from 'lodash-es'; -import { Subscription } from 'rxjs'; -import { writeText } from 'clipboard-polyfill'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { NzMessageService } from 'ng-zorro-antd/message'; -import { Component, OnDestroy, OnInit, input, Output, EventEmitter } from '@angular/core'; -import { - I18nService, - IframeCommunicationService, - ViewService, - HttpService, - DrawerStateService -} from '@app/services'; -import { effect } from '@angular/core'; - -import type { OnlineUsers } from '@app/model'; - -interface ExpiredOption { - value: number; -} - -interface ShareUserOptions { - id: string; - name: string; - username: string; -} - -interface PermissionOption { - labelKey: string; - value: string; -} - -interface ShareLinkRequest { - expired_time: number; - action_permission: 'writable' | 'readonly'; - users: string[]; -} - -@Component({ - standalone: false, - selector: 'elements-session-share', - templateUrl: 'session-share.component.html', - styleUrls: ['session-share.component.scss'] -}) -export class ElementSessionShareComponent implements OnInit, OnDestroy { - currentViewId = input(''); - hasConnected = input(false); - onLineUsers = input([]); - currentSessionId = input(null); - - @Output() onLineUsersAdd = new EventEmitter(); - @Output() onShareUserRemove = new EventEmitter(); - - private readonly DEFAULT_SHARE_REQUEST: ShareLinkRequest = { - expired_time: 10, - action_permission: 'writable', - users: [] - }; - - private iframeMessageSubscription: Subscription; - private drawerMessageSubscription: Subscription; - - shareLinkForm: FormGroup; - shareExpiredOptions: ExpiredOption[] = []; - shareUserPermissions: PermissionOption[] = []; - shareLinkRequest: ShareLinkRequest = { ...this.DEFAULT_SHARE_REQUEST }; - - loading = false; - - shareLink = ''; - shareCode = ''; - currentSessionIds = new Map(); - - searchedUserList: ShareUserOptions[] = []; - - showLinkResult = false; - showCreateShareLinkModal = false; - showCreateShareLinkForm = true; - - constructor( - private _i18n: I18nService, - private readonly _fb: FormBuilder, - private readonly _http: HttpService, - private readonly _viewSvc: ViewService, - private readonly _message: NzMessageService, - private readonly _iframeSvc: IframeCommunicationService, - private readonly _drawerStateService: DrawerStateService - ) { - // 在构造函数中初始化翻译选项 - this.initializeShareOptions(); - - // 监听 currentSessionId 的变化 - effect(() => { - const viewId = this.currentViewId(); - const sessionId = this.currentSessionId(); - - if (viewId && sessionId) { - this.currentSessionIds.set(viewId, sessionId); - } - }); - } - - ngOnInit(): void { - this.subscribeMessages(); - this.initializeShareLinkForm(); - } - ngOnDestroy(): void {} - - copyLink(event?: Event) { - event?.preventDefault(); - event?.stopPropagation(); - - const linkTitle = this._i18n.instant('LinkAddr'); - const codeTitle = this._i18n.instant('VerifyCode'); - const text = `${linkTitle}: ${this.shareLink}\n${codeTitle}: ${this.shareCode}`; - - writeText(text); - this._message.success(this._i18n.instant('Copied')); - this.resetShareLinkModal(); - } - - handleCancel() { - this.resetShareLinkModal(); - } - - onCreateShareLink() { - const formValue = this.shareLinkForm.value; - - this.shareLinkRequest = { - ...this.shareLinkRequest, - expired_time: formValue.expired_time, - action_permission: formValue.action_permission - }; - - this.showCreateShareLinkForm = false; - - const currentViewId = this.currentViewId(); - const sessionId = this.currentSessionIds.get(currentViewId); - - this._iframeSvc.sendMessage({ - name: 'SHARE_CODE_REQUEST', - data: { - requestData: this.shareLinkRequest, - sessionId: sessionId - } - }); - } - - onDeleteShareUser(item: OnlineUsers) { - this._iframeSvc.sendMessage({ - name: 'SHARE_USER_REMOVE', - data: item - }); - - if (this.currentViewId() && this._viewSvc.currentView?.id === this.currentViewId()) { - this.onShareUserRemove.emit(item); - } - } - - onShowCreateShareLinkModal() { - this.resetShareLinkModal(); - this.showCreateShareLinkModal = true; - } - - private subscribeMessages() { - this.iframeMessageSubscription = this._iframeSvc.message$.subscribe(message => { - this.handleIframeMessage(message); - }); - this.drawerMessageSubscription = this._drawerStateService.message$.subscribe(message => { - this.handleIframeMessage(message); - }); - } - - private resetShareLinkModal() { - this.showLinkResult = false; - this.showCreateShareLinkForm = true; - this.showCreateShareLinkModal = false; - this.shareLink = ''; - this.shareCode = ''; - this.searchedUserList = []; - - this.shareLinkForm.reset(); - this.shareLinkForm.patchValue({ - expired_time: this.DEFAULT_SHARE_REQUEST.expired_time, - action_permission: this.DEFAULT_SHARE_REQUEST.action_permission, - users: [] - }); - } - - onShareUserSearch(value: string) { - this.loading = true; - this._http.getShareUserList(value).subscribe({ - next: res => { - this.searchedUserList = res || []; - this.loading = false; - }, - error: error => { - console.error('Failed to search users:', error); - this.loading = false; - this.searchedUserList = []; - } - }); - } - - private initializeShareOptions() { - this.shareUserPermissions = [ - { labelKey: 'Writable', value: 'writable' }, - { labelKey: 'ReadOnly', value: 'readonly' } - ]; - - this.shareExpiredOptions = [1, 5, 10, 20, 60].map(minutes => ({ - value: minutes, - labelKey: `${minutes === 1 ? 'Minute' : 'Minutes'}` - })); - } - - private initializeShareLinkForm() { - this.shareLinkForm = this._fb.group({ - expired_time: [this.shareLinkRequest.expired_time], - action_permission: [this.shareLinkRequest.action_permission], - users: [this.shareLinkRequest.users] - }); - - this.shareLinkForm.get('users')?.valueChanges.subscribe(value => { - this.handleUsersChange(value); - }); - } - - private handleIframeMessage(message) { - const messageHandlers = { - SHARE_USER_ADD: this.handleShareUserAdd.bind(this), - - SHARE_USER_LEAVE: this.handleShareUserLeave.bind(this), - SHARE_CODE_RESPONSE: this.handleShareCodeResponse.bind(this) - }; - - const handler = messageHandlers[message.name]; - if (handler) { - handler(message.data); - } - } - - private handleShareUserAdd(data: string) { - try { - const messageData: OnlineUsers = JSON.parse(data); - - this.currentSessionIds.set(this.currentViewId(), messageData.sessionId); - this.onLineUsersAdd.emit(messageData); - } catch (error) { - console.error('Failed to parse SHARE_USER_ADD:', error); - } - } - - private generateShareLink(shareId: string) { - const baseUrl = this._viewSvc.currentView.smartEndpoint.getUrl(); - const componentType = this._viewSvc.currentView.connectMethod.component; - - switch (componentType) { - case 'koko': - this.shareLink = `${baseUrl}/luna/share/${shareId}`; - break; - case 'lion': - this.shareLink = `${baseUrl}/luna/share/${shareId}?type=lion`; - break; - default: - this.shareLink = `${baseUrl}/luna/share/${shareId}`; - break; - } - } - - private handleShareUserLeave(data: string) { - const messageData: OnlineUsers = JSON.parse(data); - - if (this.currentViewId() && this._viewSvc.currentView?.id === this.currentViewId()) { - this.onShareUserRemove.emit(messageData); - } - } - - private handleShareCodeResponse(data: string) { - try { - const messageData = JSON.parse(data); - - this.shareCode = messageData.code; - this.generateShareLink(messageData.share_id); - - this.showCreateShareLinkForm = false; - this.showLinkResult = true; - } catch (error) { - console.error('Failed to parse SHARE_CODE_RESPONSE:', error); - } - } - - private handleUsersChange(users: ShareUserOptions[]) { - this.shareLinkRequest.users = (users || []).map(user => user.id); - } - - readonly debounceSearch = _.debounce((value: string) => { - this.onShareUserSearch(value); - }, 500); -} diff --git a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.html b/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.html deleted file mode 100644 index 2b2bb6477..000000000 --- a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.html +++ /dev/null @@ -1,28 +0,0 @@ -
- - - {{ 'AvailableHotkeys' | translate }} - - - -
- @for (shortcut of shortcutKeys; track shortcut.label) { - -
-
- -
-
-
- {{ shortcut.label | translate }} -
-
-
-
- } -
-
diff --git a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.scss b/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.scss deleted file mode 100644 index da29845ff..000000000 --- a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.scss +++ /dev/null @@ -1,209 +0,0 @@ -.shortcut-keys-container { - background-color: var(--el-drawer-bg-color); - min-height: 100%; - - .ant-divider { - margin: 0 0 12px 0; - border-top-color: var(--el-divider-border-color); - - ::ng-deep .ant-divider-inner-text { - display: flex; - align-items: center; - gap: 8px; - padding: 0 12px; - font-size: 14px; - font-weight: 500; - color: var(--el-text-color-light); - } - } - - .shortcut-keys-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 12px; - margin-bottom: 24px; - - .shortcut-key-card { - background-color: var(--el-drawer-collapse-bg-color); - border: 1px solid var(--el-border-color-x); - border-radius: 6px; - cursor: pointer; - transition: all 0.3s ease; - - &:hover:not(.disabled) { - background-color: var(--el-dropdown-hover-bg-color); - border-color: var(--el-form-hover-border-color); - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - } - - &.disabled { - opacity: 0.5; - cursor: not-allowed; - - .shortcut-key-content { - .shortcut-key-icon { - background-color: var(--el-disabled-bg-color); - - nz-icon { - color: var(--el-disabled-text-color); - } - } - - .shortcut-key-info { - .shortcut-key-label { - color: var(--el-disabled-text-color); - } - - .shortcut-key-combination { - .key-tag { - background-color: var(--el-disabled-bg-color); - border-color: var(--el-disabled-border-color); - color: var(--el-disabled-text-color); - } - } - } - } - } - - ::ng-deep .ant-card-body { - padding: 6px 12px; - } - - .shortcut-key-content { - display: flex; - align-items: center; - gap: 12px; - - .shortcut-key-icon { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - background-color: var(--el-segmented-bg-color); - border-radius: 6px; - flex-shrink: 0; - - nz-icon { - font-size: 18px; - color: var(--el-icon-color); - } - } - - .shortcut-key-info { - flex: 1; - min-width: 0; - - .shortcut-key-label { - font-size: 14px; - font-weight: 500; - color: var(--el-text-color-light); - margin-bottom: 6px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .shortcut-key-combination { - display: flex; - align-items: center; - gap: 3px; - flex-wrap: wrap; - - .key-tag { - background-color: var(--el-segmented-hover-bg-color); - border: 1px solid var(--el-form-hover-border-color); - color: var(--el-text-color-light); - font-family: 'Courier New', monospace; - font-weight: 600; - font-size: 11px; - padding: 1px 6px; - border-radius: 3px; - min-width: 20px; - text-align: center; - line-height: 1.2; - - &.ant-tag { - margin: 0; - } - } - - .plus { - color: var(--el-text-color-light); - opacity: 0.6; - font-size: 11px; - margin: 0 1px; - } - } - } - } - } - } - - ::ng-deep nz-divider { - .ant-divider { - border-color: var(--el-divider-border-color); - margin: 16px 0; - - .ant-divider-inner-text { - color: var(--el-text-color-light); - font-size: 13px; - - nz-icon { - margin-right: 6px; - color: var(--el-banner-icon-color); - } - } - } - } -} - -@media (max-width: 768px) { - .shortcut-keys-container { - padding: 12px; - - .shortcut-keys-grid { - grid-template-columns: 1fr; - gap: 10px; - } - - .shortcut-key-card { - .shortcut-key-content { - gap: 10px; - - .shortcut-key-icon { - width: 32px; - height: 32px; - - nz-icon { - font-size: 16px; - } - } - - .shortcut-key-info { - .shortcut-key-label { - font-size: 13px; - } - - .shortcut-key-combination { - .key-tag { - font-size: 10px; - padding: 1px 4px; - } - } - } - } - } - } -} - -@media (prefers-color-scheme: dark) { - .shortcut-keys-container { - .shortcut-key-card { - &:hover:not(.disabled) { - box-shadow: 0 2px 8px rgba(255, 255, 255, 0.05); - } - } - } -} diff --git a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.ts b/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.ts deleted file mode 100644 index 10ae6a871..000000000 --- a/src/app/elements/drawer/shortcut-keys/shortcut-keys.component.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Component, OnInit, Input } from '@angular/core'; -import { IframeCommunicationService } from '@app/services'; - -interface ShortcutKey { - icon: string; - label: string; - keywords: string[]; - click: () => void; -} - -@Component({ - standalone: false, - selector: 'elements-shortcut-keys', - templateUrl: 'shortcut-keys.component.html', - styleUrls: ['shortcut-keys.component.scss'] -}) -export class ElementShortcutKeysComponent implements OnInit { - @Input() currentViewId: string | null = null; - @Input() terminalContent: any = null; - - shortcutKeys: ShortcutKey[] = [ - { - icon: 'stop', - label: 'Cancel + C', - keywords: ['Ctrl', 'C'], - click: () => { - this.sendShortcutKey('\x03'); - } - }, - { - icon: 'arrow-up', - label: 'UpArrow', - keywords: ['↑'], - click: () => { - this.sendShortcutKey('\x1b[A'); - } - }, - { - icon: 'arrow-down', - label: 'DownArrow', - keywords: ['↓'], - click: () => { - this.sendShortcutKey('\x1b[B'); - } - }, - { - icon: 'arrow-left', - label: 'LeftArrow', - keywords: ['←'], - click: () => { - this.sendShortcutKey('\x1b[D'); - } - }, - { - icon: 'arrow-right', - label: 'RightArrow', - keywords: ['→'], - click: () => { - this.sendShortcutKey('\x1b[C'); - } - } - ]; - - constructor(private readonly _iframeSvc: IframeCommunicationService) {} - - ngOnInit(): void {} - - get isTerminalConnected(): boolean { - const connected = Boolean(this.currentViewId && this.terminalContent); - - return connected; - } - - get currentTerminalId(): string | null { - return this.getTerminalIdByViewId(); - } - - sendShortcutKey(key: string) { - const terminalId = this.getTerminalIdByViewId(); - - if (!terminalId) { - return; - } - - this._iframeSvc.sendMessage({ - name: 'CMD', - data: key, - }); - } - - private getTerminalIdByViewId(): string | null { - if (!this.currentViewId || !this.terminalContent) { - return null; - } - - if (this.terminalContent.terminalId) { - return this.terminalContent.terminalId; - } - - // 兼容其他可能的字段名称 - if (typeof this.terminalContent === 'object') { - const possibleFields = ['terminal_id', 'sessionId', 'session_id', 'id']; - - for (const field of possibleFields) { - if (this.terminalContent[field]) { - return this.terminalContent[field]; - } - } - } - - return null; - } -} diff --git a/src/app/elements/elements.component.ts b/src/app/elements/elements.component.ts index 8887d2fef..89b7aa895 100644 --- a/src/app/elements/elements.component.ts +++ b/src/app/elements/elements.component.ts @@ -41,10 +41,6 @@ import {ElementFaceMonitorComponent} from '@app/elements/connect/face-monitor/fa import {ElementACLDialogComponent} from '@app/services/connect-token/acl-dialog/acl-dialog.component'; import {ElementDialogAlertComponent} from '@app/services/dialog/alert.service'; import {ElementTermComponent} from '@app/elements/replay/term/term.component'; -import {ElementDrawerComponent} from '@app/elements/drawer/drawer.component'; -import {ElementSessionShareComponent} from '@app/elements/drawer/session-share/session-share.component'; -import {ElementFileManagerComponent} from '@app/elements/drawer/filemanager/filemanager.component'; -import {ElementShortcutKeysComponent} from '@app/elements/drawer/shortcut-keys/shortcut-keys.component'; export const ElementComponents = [ ElementLeftBarComponent, @@ -85,8 +81,4 @@ export const ElementComponents = [ ElementACLDialogComponent, ElementDialogAlertComponent, ElementTermComponent, - ElementDrawerComponent, - ElementSessionShareComponent, - ElementFileManagerComponent, - ElementShortcutKeysComponent ]; diff --git a/src/app/elements/nav/nav.component.ts b/src/app/elements/nav/nav.component.ts index 51114023a..22c6ca8fa 100644 --- a/src/app/elements/nav/nav.component.ts +++ b/src/app/elements/nav/nav.component.ts @@ -7,7 +7,7 @@ import { Component, OnInit } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; import { NzModalService } from 'ng-zorro-antd/modal'; import { ElementSettingComponent } from '@app/elements/nav/setting/setting.component'; -import { DrawerStateService, HttpService, SettingService, ViewService } from '@app/services'; +import { IframeCommunicationService, HttpService, SettingService, ViewService } from '@app/services'; @Component({ standalone: false, @@ -29,7 +29,7 @@ export class ElementNavComponent implements OnInit { private _dialog: NzModalService, private _settingSvc: SettingService, private _http: HttpService, - private _drawerStateService: DrawerStateService, + private _iframeSvc: IframeCommunicationService, ) {} get viewListSorted() { @@ -161,7 +161,7 @@ export class ElementNavComponent implements OnInit { id: theme.name, click: () => { useTheme().switchTheme(theme.name); - this._drawerStateService.sendComponentMessage({ + this._iframeSvc.sendMessage({ name: 'CHANGE_MAIN_THEME', data: theme.name }); @@ -226,10 +226,4 @@ export class ElementNavComponent implements OnInit { onJumpUi() { window.open('/ui/', '_blank'); } - - openChat() { - this._drawerStateService.sendComponentMessage({ - name: 'SEND_CHAT_IFRAME' - }); - } } diff --git a/src/app/pages/connect/connect.component.html b/src/app/pages/connect/connect.component.html index d116b93c6..53aa378fa 100644 --- a/src/app/pages/connect/connect.component.html +++ b/src/app/pages/connect/connect.component.html @@ -56,7 +56,6 @@
-
@@ -153,7 +152,6 @@
-