diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java b/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java index c0de57e7..e9a715b7 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java @@ -110,7 +110,10 @@ public class SessionStores { } } // 超时时间 - session.timeout(config.getTimeout()); + Integer timeout = config.getTimeout(); + if (timeout != null) { + session.timeout(timeout); + } return session; } diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java index 4717c811..691f400d 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java @@ -71,7 +71,7 @@ public class HostSshConfigDTO implements GenericsDataModel, UpdatePasswordAction private Long keyId; @NotNull - @Min(value = 1) + @Min(value = 0) @Max(value = 100000) @Schema(description = "连接超时时间") private Integer connectTimeout; diff --git a/orion-visor-ui/src/views/terminal/components/view/guacd/actions/clipboard-action.vue b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/clipboard-action.vue new file mode 100644 index 00000000..884875e3 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/clipboard-action.vue @@ -0,0 +1,61 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/view/guacd/actions/combination-key-action.vue b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/combination-key-action.vue new file mode 100644 index 00000000..d5be4aa8 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/combination-key-action.vue @@ -0,0 +1,37 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/view/guacd/actions/display-action.vue b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/display-action.vue new file mode 100644 index 00000000..b10fb9b6 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/display-action.vue @@ -0,0 +1,91 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/view/guacd/actions/info-action.vue b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/info-action.vue new file mode 100644 index 00000000..a00caf41 --- /dev/null +++ b/orion-visor-ui/src/views/terminal/components/view/guacd/actions/info-action.vue @@ -0,0 +1,48 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue index 9408cb95..7d96f1fc 100644 --- a/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue +++ b/orion-visor-ui/src/views/terminal/components/view/rdp/rdp-action-bar.vue @@ -25,9 +25,9 @@ :show-arrow="false" :content="action.content"> + @click="toggleAction(action.item)"> @@ -35,65 +35,28 @@ + +
+ +
-
- - - 分辨率 - - - - +
+
- - - {{ item.name }} - - + +
+ +
+
- - - +
- -
+ +
+ +
@@ -130,44 +95,31 @@ import type { IRdpSession } from '@/views/terminal/interfaces'; import { TerminalStatus, - GuacdCombinationKeyItems, GuacdActionItemKeys, RdpActionBarItems, - screenResolutionKey, - fitDisplayValue, ActionBarPosition + ActionBarPosition, TerminalSessionTypes } from '@/views/terminal/types/const'; import { computed, ref, watch, onMounted } from 'vue'; - import { setAutoFocus } from '@/utils/dom'; import { saveAs } from 'file-saver'; - import { readText } from '@/hooks/copy'; - import { useTerminalStore, useDictStore } from '@/store'; - import useGuacdActionBar from '@/views/terminal/types/use-guacd-action-bar'; + import { useTerminalStore } from '@/store'; import useVisible from '@/hooks/visible'; + import InfoAction from '../guacd/actions/info-action.vue'; + import DisplayAction from '../guacd/actions/display-action.vue'; + import TriggerKeyAction from '../guacd/actions/trigger-key-action.vue'; + import ClipboardAction from '../guacd/actions/clipboard-action.vue'; + import CombinationKeyAction from '../guacd/actions/combination-key-action.vue'; + import SftpUploadModal from '@/views/terminal/components/view/sftp/sftp-upload-modal.vue'; const props = defineProps<{ session: IRdpSession; direction: string; }>(); - const { preference, transferManager } = useTerminalStore(); - const { toOptions, getDictValue } = useDictStore(); + const { hosts, preference, openSession, reOpenSession, transferManager } = useTerminalStore(); const { visible, setVisible } = useVisible(); - const { - displaySize, - clipboardData, - fitOnce, - setDisplaySize, - triggerCombinationKey, - sendClipboardData, - clearClipboardData, - disconnect, - } = useGuacdActionBar({ - session: props.session, - setVisible, - }); - const current = ref(''); + const sftpUploadModalRef = ref(); const fileList = ref([]); const actions = computed(() => { @@ -178,7 +130,17 @@ return { ...item, active: current.value === key, - disabled: (key === GuacdActionItemKeys.DISPLAY || key === GuacdActionItemKeys.SAVE_RDP || GuacdActionItemKeys.DISCONNECT || key === GuacdActionItemKeys.CLOSE) ? false : !props.session.isWriteable(), + enabled: () => { + if (key === GuacdActionItemKeys.DISPLAY || key === GuacdActionItemKeys.DISCONNECT || key === GuacdActionItemKeys.RECONNECT || key === GuacdActionItemKeys.SAVE_RDP || key === GuacdActionItemKeys.CLOSE) { + return true; + } else if (key === GuacdActionItemKeys.OPEN_SFTP || key === GuacdActionItemKeys.SFTP_UPLOAD) { + // 支持 SFTP 协议 + if (!hosts.hostList.find(s => s.id === props.session.info.hostId)?.types?.includes?.(TerminalSessionTypes.SFTP.protocol)) { + return false; + } + } + return props.session.isWriteable(); + } }; }); }); @@ -189,31 +151,22 @@ return; } // 重新触发点击 - toggleClickAction(current.value); + toggleAction(current.value); }); // 触发 action - const toggleClickAction = (key: string) => { - if (key === GuacdActionItemKeys.DISPLAY) { - // 显示设置 - current.value = GuacdActionItemKeys.DISPLAY; - if (props.session.displayHandler?.autoFit) { - displaySize.value = fitDisplayValue; - } else { - displaySize.value = `${props.session.displayHandler?.displayWidth || 0}x${props.session.displayHandler?.displayHeight || 0}`; - } - } else if (key === GuacdActionItemKeys.COMBINATION_KEY) { - // 组合键 - current.value = GuacdActionItemKeys.COMBINATION_KEY; - } else if (key === GuacdActionItemKeys.CLIPBOARD) { - // 剪切板 - current.value = GuacdActionItemKeys.CLIPBOARD; - readText(false) - .then(s => clipboardData.value = s) - .catch(() => clipboardData.value = ''); - } else if (key === GuacdActionItemKeys.UPLOAD) { - // 文件上传 - current.value = GuacdActionItemKeys.UPLOAD; + const toggleAction = (key: string) => { + if (key === GuacdActionItemKeys.OPEN_SFTP) { + // 打开 SFTP 会话 + openSession(hosts.hostList.find(s => s.id === props.session.info.hostId) as any, TerminalSessionTypes.SFTP); + setVisible(false); + } else if (key === GuacdActionItemKeys.SFTP_UPLOAD) { + // 打开 SFTP 上传 + sftpUploadModalRef.value.open('/'); + setVisible(false); + } else if (key === GuacdActionItemKeys.RDP_UPLOAD) { + // RDP 文件上传 + current.value = GuacdActionItemKeys.RDP_UPLOAD; fileList.value = []; } else if (key === GuacdActionItemKeys.SAVE_RDP) { // 保存 rdp 文件 @@ -221,9 +174,14 @@ } else if (key === GuacdActionItemKeys.DISCONNECT) { // 断开连接 disconnect(); + } else if (key === GuacdActionItemKeys.RECONNECT) { + // 重新连接 + reconnect(); } else if (key === GuacdActionItemKeys.CLOSE) { // 关闭工具栏 setVisible(false); + } else { + current.value = key; } }; @@ -256,6 +214,29 @@ fileList.value = []; }; + // 关闭会话 + const disconnect = () => { + props.session.disconnect(); + setVisible(false); + }; + + // 关闭会话 + const reconnect = () => { + const session = props.session; + // 断开连接 + session.disconnect(); + // 重新连接 + if (session.state.canReconnect) { + reOpenSession(session.sessionKey); + } + setVisible(false); + }; + + // 关闭 + const close = () => { + setVisible(false); + }; + // 设置选中 onMounted(() => { if (actions.value?.length) { diff --git a/orion-visor-ui/src/views/terminal/components/view/vnc/vnc-action-bar.vue b/orion-visor-ui/src/views/terminal/components/view/vnc/vnc-action-bar.vue index 90992586..d3f978ab 100644 --- a/orion-visor-ui/src/views/terminal/components/view/vnc/vnc-action-bar.vue +++ b/orion-visor-ui/src/views/terminal/components/view/vnc/vnc-action-bar.vue @@ -25,9 +25,9 @@ :show-arrow="false" :content="action.content"> + @click="toggleAction(action.item)"> @@ -35,65 +35,35 @@
+ +
+ +
-
- - - 分辨率 - - - - +
+
- - - {{ item.name }} - - + +
+ +
+
- - - +
+ +
@@ -107,43 +77,31 @@ import type { IVncSession } from '@/views/terminal/interfaces'; import { TerminalStatus, - GuacdCombinationKeyItems, GuacdActionItemKeys, VncActionBarItems, - screenResolutionKey, - fitDisplayValue, ActionBarPosition, + ActionBarPosition, + TerminalSessionTypes, } from '@/views/terminal/types/const'; import { computed, ref, watch, onMounted } from 'vue'; - import { setAutoFocus } from '@/utils/dom'; - import { readText } from '@/hooks/copy'; - import { useTerminalStore, useDictStore } from '@/store'; - import useGuacdActionBar from '@/views/terminal/types/use-guacd-action-bar'; + import { useTerminalStore } from '@/store'; import useVisible from '@/hooks/visible'; + import InfoAction from '../guacd/actions/info-action.vue'; + import DisplayAction from '../guacd/actions/display-action.vue'; + import ClipboardAction from '../guacd/actions/clipboard-action.vue'; + import TriggerKeyAction from '../guacd/actions/trigger-key-action.vue'; + import CombinationKeyAction from '../guacd/actions/combination-key-action.vue'; + import SftpUploadModal from '@/views/terminal/components/view/sftp/sftp-upload-modal.vue'; const props = defineProps<{ session: IVncSession; direction: string; }>(); - const { preference } = useTerminalStore(); - const { toOptions, getDictValue } = useDictStore(); + const { hosts, preference, openSession, reOpenSession } = useTerminalStore(); const { visible, setVisible } = useVisible(); - const { - displaySize, - clipboardData, - fitOnce, - setDisplaySize, - triggerCombinationKey, - sendClipboardData, - clearClipboardData, - disconnect, - } = useGuacdActionBar({ - session: props.session, - setVisible, - }); - const current = ref(''); + const sftpUploadModalRef = ref(); const actions = computed(() => { return VncActionBarItems.filter(item => { @@ -153,7 +111,16 @@ return { ...item, active: current.value === key, - disabled: (key === GuacdActionItemKeys.DISPLAY || GuacdActionItemKeys.DISCONNECT || key === GuacdActionItemKeys.CLOSE) ? false : !props.session.isWriteable(), + enabled: () => { + if (key === GuacdActionItemKeys.DISPLAY || key === GuacdActionItemKeys.DISCONNECT || key === GuacdActionItemKeys.RECONNECT || key === GuacdActionItemKeys.CLOSE) { + return true; + } else if (key === GuacdActionItemKeys.OPEN_SFTP || key === GuacdActionItemKeys.SFTP_UPLOAD) { + if (!hosts.hostList.find(s => s.id === props.session.info.hostId)?.types?.includes?.(TerminalSessionTypes.SFTP.protocol)) { + return false; + } + } + return props.session.isWriteable(); + } }; }); }); @@ -163,38 +130,57 @@ if (!val) { return; } - // 重新触发点击 - toggleClickAction(current.value); + // 重新触发 + toggleAction(current.value); }); // 触发 action - const toggleClickAction = (key: string) => { - if (key === GuacdActionItemKeys.DISPLAY) { - // 显示设置 - current.value = GuacdActionItemKeys.DISPLAY; - if (props.session.displayHandler?.autoFit) { - displaySize.value = fitDisplayValue; - } else { - displaySize.value = `${props.session.displayHandler?.displayWidth || 0}x${props.session.displayHandler?.displayHeight || 0}`; - } - } else if (key === GuacdActionItemKeys.COMBINATION_KEY) { - // 组合键 - current.value = GuacdActionItemKeys.COMBINATION_KEY; - } else if (key === GuacdActionItemKeys.CLIPBOARD) { - // 剪切板 - current.value = GuacdActionItemKeys.CLIPBOARD; - readText(false) - .then(s => clipboardData.value = s) - .catch(() => clipboardData.value = ''); + const toggleAction = (key: string) => { + if (key === GuacdActionItemKeys.OPEN_SFTP) { + // 打开 SFTP 会话 + openSession(hosts.hostList.find(s => s.id === props.session.info.hostId) as any, TerminalSessionTypes.SFTP); + setVisible(false); + } else if (key === GuacdActionItemKeys.SFTP_UPLOAD) { + // 打开 SFTP 上传 + sftpUploadModalRef.value.open('/'); + setVisible(false); } else if (key === GuacdActionItemKeys.DISCONNECT) { // 断开连接 disconnect(); + } else if (key === GuacdActionItemKeys.RECONNECT) { + // 重新连接 + reconnect(); } else if (key === GuacdActionItemKeys.CLOSE) { // 关闭工具栏 setVisible(false); + } else { + current.value = key; } }; + // 关闭会话 + const disconnect = () => { + props.session.disconnect(); + setVisible(false); + }; + + // 关闭会话 + const reconnect = () => { + const session = props.session; + // 断开连接 + session.disconnect(); + // 重新连接 + if (session.state.canReconnect) { + reOpenSession(session.sessionKey); + } + setVisible(false); + }; + + // 关闭 + const close = () => { + setVisible(false); + }; + // 设置选中 onMounted(() => { if (actions.value?.length) { diff --git a/orion-visor-ui/src/views/terminal/types/use-guacd-action-bar.ts b/orion-visor-ui/src/views/terminal/types/use-guacd-action-bar.ts deleted file mode 100644 index 731dd0e1..00000000 --- a/orion-visor-ui/src/views/terminal/types/use-guacd-action-bar.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { ref } from 'vue'; -import type { IGuacdSession } from '../interfaces'; -import { fitDisplayValue } from './const'; -import { getDisplaySize } from './utils'; - -// guacd 工具栏配置 -export interface UseGuacdActionBarOptions { - session: IGuacdSession; - setVisible: (visible: boolean) => void; -} - -// 使用主机配置表单 -export default function useGuacdActionBar(options: UseGuacdActionBarOptions) { - const { session, setVisible } = options; - - const displaySize = ref(fitDisplayValue); - const clipboardData = ref(''); - - // 临时自适应 - const fitOnce = () => { - session.displayHandler?.fit(true); - setVisible(false); - }; - - // 设置显示大小 - const setDisplaySize = () => { - const displayHandler = session.displayHandler; - if (!displayHandler) { - return; - } - if (displaySize.value === fitDisplayValue) { - // 设置自适应 - displayHandler.autoFit = true; - displayHandler.fit(true); - } else { - try { - // 获取大小 - const [width, height] = getDisplaySize(displaySize.value, true); - // 取消自适应 - displayHandler.autoFit = false; - // 设置大小 - displayHandler.resize(width, height); - } catch (e) { - return; - } - } - setVisible(false); - }; - - // 触发组合键 - const triggerCombinationKey = (keys: Array) => { - session.sendKeys(keys); - setVisible(false); - }; - - // 发送剪切板数据 - const sendClipboardData = () => { - // 粘贴 - session.paste(clipboardData.value); - setVisible(false); - }; - - // 清空剪切板数据 - const clearClipboardData = () => { - clipboardData.value = ''; - }; - - // 关闭会话 - const disconnect = () => { - session.disconnect(); - setVisible(false); - }; - - return { - displaySize, - clipboardData, - fitOnce, - setDisplaySize, - triggerCombinationKey, - sendClipboardData, - clearClipboardData, - disconnect, - }; -}