feat: 终端截屏.
This commit is contained in:
@@ -19,9 +19,27 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction } from '../../types/terminal.type';
|
import type { SidebarAction } from '../../types/terminal.type';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
import { TerminalTabType } from '../../types/terminal.const';
|
||||||
import IconActions from './icon-actions.vue';
|
import IconActions from './icon-actions.vue';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer', 'screenshot']);
|
const emits = defineEmits(['openSnippet', 'openSftp', 'openTransfer']);
|
||||||
|
|
||||||
|
const { tabManager, sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
|
// 终端截屏
|
||||||
|
const screenshot = () => {
|
||||||
|
const tab = tabManager.getCurrentTab();
|
||||||
|
if (!tab || tab.type !== TerminalTabType.TERMINAL) {
|
||||||
|
Message.warning('请切换到终端标签页');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取处理器并截图
|
||||||
|
sessionManager.getSession(tab.key)
|
||||||
|
?.handler
|
||||||
|
?.screenshot();
|
||||||
|
};
|
||||||
|
|
||||||
// 顶部操作
|
// 顶部操作
|
||||||
const topActions = [
|
const topActions = [
|
||||||
@@ -50,7 +68,7 @@
|
|||||||
{
|
{
|
||||||
icon: 'icon-camera',
|
icon: 'icon-camera',
|
||||||
content: '截图',
|
content: '截图',
|
||||||
click: () => emits('screenshot')
|
click: screenshot
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import type { Terminal } from 'xterm';
|
|||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { InnerTabs } from '../types/terminal.const';
|
import { InnerTabs } from '../types/terminal.const';
|
||||||
|
import html2canvas from 'html2canvas';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
const { copy: copyValue, readText } = useCopy();
|
const { copy: copyValue, readText } = useCopy();
|
||||||
|
|
||||||
@@ -178,6 +181,29 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
|||||||
this.session.disconnect();
|
this.session.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 截图
|
||||||
|
async screenshot() {
|
||||||
|
try {
|
||||||
|
// 获取截屏
|
||||||
|
const canvas = await html2canvas(this.inst.element as HTMLElement, {
|
||||||
|
useCORS: true,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
});
|
||||||
|
// 保存图片
|
||||||
|
const blob = await new Promise((resolve, reject) => {
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
resolve(blob);
|
||||||
|
}, 'image/png');
|
||||||
|
});
|
||||||
|
saveAs(blob as Blob, `screenshot-${Date.now()}.png`);
|
||||||
|
} catch (e) {
|
||||||
|
Message.error('保存失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭 tab
|
// 关闭 tab
|
||||||
closeTab() {
|
closeTab() {
|
||||||
this.tabManager.deleteTab(this.session.sessionId);
|
this.tabManager.deleteTab(this.session.sessionId);
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
}
|
}
|
||||||
if (preference.pluginsSetting.enableWebglPlugin) {
|
if (preference.pluginsSetting.enableWebglPlugin) {
|
||||||
// WebGL 渲染插件
|
// WebGL 渲染插件
|
||||||
this.addons.webgl = new WebglAddon();
|
this.addons.webgl = new WebglAddon(true);
|
||||||
} else {
|
} else {
|
||||||
// canvas 渲染插件
|
// canvas 渲染插件
|
||||||
this.addons.canvas = new CanvasAddon();
|
this.addons.canvas = new CanvasAddon();
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ export default class TerminalTabManager implements ITerminalTabManager {
|
|||||||
this.items = [InnerTabs.NEW_CONNECTION];
|
this.items = [InnerTabs.NEW_CONNECTION];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取当前 tab
|
||||||
|
getCurrentTab() {
|
||||||
|
if (!this.active) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return this.items.find(s => s.key === this.active);
|
||||||
|
}
|
||||||
|
|
||||||
// 点击 tab
|
// 点击 tab
|
||||||
clickTab(key: string): void {
|
clickTab(key: string): void {
|
||||||
this.active = key;
|
this.active = key;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<terminal-left-sidebar />
|
<terminal-left-sidebar />
|
||||||
</div>
|
</div>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<div class="host-layout-content" ref="content">
|
<div class="host-layout-content">
|
||||||
<!-- 主机加载中骨架 -->
|
<!-- 主机加载中骨架 -->
|
||||||
<loading-skeleton v-if="contentLoading" />
|
<loading-skeleton v-if="contentLoading" />
|
||||||
<!-- 终端内容区域 -->
|
<!-- 终端内容区域 -->
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 右侧操作栏 -->
|
<!-- 右侧操作栏 -->
|
||||||
<div class="host-layout-right">
|
<div class="host-layout-right">
|
||||||
<terminal-right-sidebar @screenshot="screenshotTerminal" />
|
<terminal-right-sidebar />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,11 +41,8 @@
|
|||||||
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
||||||
import TerminalContent from './components/layout/terminal-content.vue';
|
import TerminalContent from './components/layout/terminal-content.vue';
|
||||||
import LoadingSkeleton from './components/layout/loading-skeleton.vue';
|
import LoadingSkeleton from './components/layout/loading-skeleton.vue';
|
||||||
import html2canvas from 'html2canvas';
|
|
||||||
import { saveAs } from 'file-saver';
|
|
||||||
import './assets/styles/layout.less';
|
import './assets/styles/layout.less';
|
||||||
import 'xterm/css/xterm.css';
|
import 'xterm/css/xterm.css';
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
const terminalStore = useTerminalStore();
|
const terminalStore = useTerminalStore();
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
@@ -54,7 +51,6 @@
|
|||||||
|
|
||||||
const originTitle = document.title;
|
const originTitle = document.title;
|
||||||
const render = ref(false);
|
const render = ref(false);
|
||||||
const content = ref();
|
|
||||||
|
|
||||||
// 关闭视口处理
|
// 关闭视口处理
|
||||||
const handleBeforeUnload = (event: any) => {
|
const handleBeforeUnload = (event: any) => {
|
||||||
@@ -62,30 +58,6 @@
|
|||||||
event.returnValue = confirm('系统可能不会保存您所做的更改');
|
event.returnValue = confirm('系统可能不会保存您所做的更改');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 终端截图
|
|
||||||
// FIXME test 水印
|
|
||||||
const screenshotTerminal = async () => {
|
|
||||||
try {
|
|
||||||
console.log(content.value);
|
|
||||||
const canvas = await html2canvas(content.value, {
|
|
||||||
useCORS: true,
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
});
|
|
||||||
console.log(canvas);
|
|
||||||
const blob = await new Promise((resolve, reject) => {
|
|
||||||
canvas.toBlob((blob) => {
|
|
||||||
if (!blob) {
|
|
||||||
reject(new Error('截屏失败'));
|
|
||||||
}
|
|
||||||
resolve(blob);
|
|
||||||
}, 'image/png');
|
|
||||||
});
|
|
||||||
saveAs(blob, `screenshot-${Date.now()}.png`);
|
|
||||||
} catch (e) {
|
|
||||||
Message.error('保存失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载用户终端偏好
|
// 加载用户终端偏好
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
await terminalStore.fetchPreference();
|
await terminalStore.fetchPreference();
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ export interface ITerminalTabManager {
|
|||||||
// 全部 tab
|
// 全部 tab
|
||||||
items: Array<TerminalTabItem>;
|
items: Array<TerminalTabItem>;
|
||||||
|
|
||||||
|
// 获取当前 tab
|
||||||
|
getCurrentTab: () => TerminalTabItem | undefined;
|
||||||
// 点击 tab
|
// 点击 tab
|
||||||
clickTab: (key: string) => void;
|
clickTab: (key: string) => void;
|
||||||
// 删除 tab
|
// 删除 tab
|
||||||
@@ -236,6 +238,8 @@ export interface ITerminalSessionHandler {
|
|||||||
clear: () => void;
|
clear: () => void;
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect: () => void;
|
disconnect: () => void;
|
||||||
|
// 截图
|
||||||
|
screenshot: () => void;
|
||||||
// 关闭 tab
|
// 关闭 tab
|
||||||
closeTab: () => void;
|
closeTab: () => void;
|
||||||
// 切换到前一个 tab
|
// 切换到前一个 tab
|
||||||
|
|||||||
Reference in New Issue
Block a user