From a0a7b18a703cdbb8d0b8e56d75c76a2418b04b41 Mon Sep 17 00:00:00 2001
From: zhaojisen <1301338853@qq.com>
Date: Fri, 27 Jun 2025 15:23:05 +0800
Subject: [PATCH 1/3] Perf: revert drawer
---
src/app/elements/chat/chat.component.ts | 9 +--------
.../content/content-window/koko/koko.component.ts | 6 ++++--
src/app/elements/nav/nav.component.ts | 12 +++---------
3 files changed, 8 insertions(+), 19 deletions(-)
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..aa571cbf4 100644
--- a/src/app/elements/content/content-window/koko/koko.component.ts
+++ b/src/app/elements/content/content-window/koko/koko.component.ts
@@ -165,10 +165,12 @@ export class ElementConnectorKokoComponent implements OnInit {
});
if (this.protocol === 'k8s') {
- return (this.iframeURL = `${this.baseUrl}/k8s/?` + query);
+ // return (this.iframeURL = `${this.baseUrl}/k8s/?` + query);
+ return (this.iframeURL = `http://localhost:9530/koko/k8s/?` + query);
}
- this.iframeURL = `${this.baseUrl}/connect/?` + query;
+ // this.iframeURL = `${this.baseUrl}/connect/?` + query;
+ this.iframeURL = `http://localhost:9530/koko/connect/?` + query;
}
generateFileManagerURL() {
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'
- });
- }
}
From c0f4b2cda940b4a99b19d0c3141482988d6e0455 Mon Sep 17 00:00:00 2001
From: zhaojisen <1301338853@qq.com>
Date: Mon, 30 Jun 2025 15:54:21 +0800
Subject: [PATCH 2/3] Remove drawer
---
src/app/elements/chat/chat.component.html | 1 -
.../content-window/koko/koko.component.ts | 5 +-
src/app/elements/drawer/drawer.component.html | 39 --
src/app/elements/drawer/drawer.component.scss | 116 ----
src/app/elements/drawer/drawer.component.ts | 413 ---------------
.../filemanager/filemanager.component.html | 29 -
.../filemanager/filemanager.component.scss | 63 ---
.../filemanager/filemanager.component.ts | 501 ------------------
.../session-share.component.html | 191 -------
.../session-share.component.scss | 69 ---
.../session-share/session-share.component.ts | 300 -----------
.../shortcut-keys.component.html | 28 -
.../shortcut-keys.component.scss | 209 --------
.../shortcut-keys/shortcut-keys.component.ts | 113 ----
src/app/elements/elements.component.ts | 8 -
src/app/pages/connect/connect.component.html | 2 -
src/app/pages/connect/connect.component.scss | 4 -
17 files changed, 2 insertions(+), 2089 deletions(-)
delete mode 100644 src/app/elements/drawer/drawer.component.html
delete mode 100644 src/app/elements/drawer/drawer.component.scss
delete mode 100644 src/app/elements/drawer/drawer.component.ts
delete mode 100644 src/app/elements/drawer/filemanager/filemanager.component.html
delete mode 100644 src/app/elements/drawer/filemanager/filemanager.component.scss
delete mode 100644 src/app/elements/drawer/filemanager/filemanager.component.ts
delete mode 100644 src/app/elements/drawer/session-share/session-share.component.html
delete mode 100644 src/app/elements/drawer/session-share/session-share.component.scss
delete mode 100644 src/app/elements/drawer/session-share/session-share.component.ts
delete mode 100644 src/app/elements/drawer/shortcut-keys/shortcut-keys.component.html
delete mode 100644 src/app/elements/drawer/shortcut-keys/shortcut-keys.component.scss
delete mode 100644 src/app/elements/drawer/shortcut-keys/shortcut-keys.component.ts
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/content/content-window/koko/koko.component.ts b/src/app/elements/content/content-window/koko/koko.component.ts
index aa571cbf4..c6894b5a5 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 },
'*'
);
}
@@ -165,8 +165,7 @@ export class ElementConnectorKokoComponent implements OnInit {
});
if (this.protocol === 'k8s') {
- // return (this.iframeURL = `${this.baseUrl}/k8s/?` + query);
- return (this.iframeURL = `http://localhost:9530/koko/k8s/?` + query);
+ return (this.iframeURL = `${this.baseUrl}/k8s/?` + query);
}
// this.iframeURL = `${this.baseUrl}/connect/?` + query;
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) {
-
-
-
- {{ 'OnlineUser' | translate }} ({{ onLineUsers().length }})
-
-
-
-
- @for (item of onLineUsers(); track item.terminal_id + item.user + item.primary) {
-
-
-
-
- {{ item.user }}
-
-
-
-
- {{ 'PrimaryUser' | translate }}
-
-
-
- {{ 'Writable' | translate }}
-
-
-
- {{ 'ReadOnly' | translate }}
-
-
-
-
-
-
-
-
-
- }
-
-
-
-
- } @else {
-
- }
-
-
-
-
- @if (showCreateShareLinkForm) {
- {{ 'CreateLink' | translate }}
- } @else {
- {{ 'ShareLink' | translate }}
- }
-
-
-
- @if (showCreateShareLinkForm) {
-
- }
-
- @if (showLinkResult) {
-
- }
-
-
-
-
-
- @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/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 @@
-
Date: Mon, 30 Jun 2025 15:54:53 +0800
Subject: [PATCH 3/3] Remove post
---
src/app/elements/content/content-window/koko/koko.component.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
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 c6894b5a5..3cda9e69d 100644
--- a/src/app/elements/content/content-window/koko/koko.component.ts
+++ b/src/app/elements/content/content-window/koko/koko.component.ts
@@ -168,8 +168,7 @@ export class ElementConnectorKokoComponent implements OnInit {
return (this.iframeURL = `${this.baseUrl}/k8s/?` + query);
}
- // this.iframeURL = `${this.baseUrl}/connect/?` + query;
- this.iframeURL = `http://localhost:9530/koko/connect/?` + query;
+ this.iframeURL = `${this.baseUrl}/connect/?` + query;
}
generateFileManagerURL() {