feat: 命令右键菜单.

This commit is contained in:
lijiahang
2024-01-25 17:43:44 +08:00
parent 3011ecee9e
commit 6ac99b692d
10 changed files with 203 additions and 62 deletions

View File

@@ -75,15 +75,19 @@ export interface TerminalShortcutSetting {
}
// 终端快捷键
export interface TerminalShortcutKey {
item: string;
enabled: boolean;
export interface ShortcutKey {
ctrlKey: boolean;
shiftKey: boolean;
altKey: boolean;
code: string;
}
// 终端快捷键
export interface TerminalShortcutKey extends ShortcutKey {
item: string;
enabled: boolean;
}
// 终端快捷键编辑
export interface TerminalShortcutKeyEditable extends TerminalShortcutKey {
editable: boolean;

View File

@@ -294,4 +294,10 @@ body[terminal-theme='dark'] .arco-modal-container {
align-items: center;
}
}
&-icon {
font-size: 16px;
margin: 0 8px 0 4px;
}
}

View File

@@ -5,7 +5,7 @@
@close="onClose">
<!-- 表头 -->
<template #title>
<span class="snippet-drawer-title">
<span class="snippet-drawer-title usn">
<icon-code />
命令片段
</span>

View File

@@ -45,20 +45,24 @@
:deep(.arco-collapse-item) {
border: none;
.arco-collapse-item-header-title {
user-select: none;
}
.arco-collapse-item-header {
&-header {
border: none;
&-title {
user-select: none;
}
&-extra {
user-select: none;
}
}
.arco-collapse-item-content {
&-content {
background-color: unset;
padding: 0;
}
.arco-collapse-item-content-box {
&-content-box {
padding: 0;
}
}

View File

@@ -1,47 +1,108 @@
<template>
<div class="snippet-item-wrapper"
:class="[!!item.expand ? 'snippet-item-wrapper-expand' : '']"
@click="expandItem">
<div class="snippet-item">
<div class="snippet-item-title">
<!-- 名称 -->
<span class="snippet-item-title-name">
<a-dropdown class="terminal-context-menu"
:popup-max-height="false"
trigger="contextMenu"
position="bl"
alignPoint>
<!-- 命令 -->
<div class="snippet-item-wrapper"
:class="[!!item.expand ? 'snippet-item-wrapper-expand' : '']"
@click="clickItem">
<div class="snippet-item">
<div class="snippet-item-title">
<!-- 名称 -->
<span class="snippet-item-title-name">
{{ item.name }}
</span>
<!-- 操作 -->
<div class="snippet-item-title-actions">
<a-space>
<!-- 粘贴 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop="paste">
<template #icon>
<icon-paste />
</template>
粘贴
</a-tag>
<!-- 执行 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop="exec">
<template #icon>
<icon-thunderbolt />
</template>
执行
</a-tag>
</a-space>
<!-- 操作 -->
<div class="snippet-item-title-actions">
<a-space>
<!-- 粘贴 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop.prevent="paste">
<template #icon>
<icon-paste />
</template>
粘贴
</a-tag>
<!-- 执行 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop="exec">
<template #icon>
<icon-thunderbolt />
</template>
执行
</a-tag>
</a-space>
</div>
</div>
</div>
<!-- 命令 -->
<span class="snippet-item-command">
<!-- 命令 -->
<span class="snippet-item-command">
{{ item.command }}
</span>
</div>
</div>
</div>
<!-- 右键菜单 -->
<template #content>
<!-- 复制 -->
<a-doption @click="copyCommand">
<div class="terminal-context-menu-icon">
<icon-copy />
</div>
<div>复制</div>
</a-doption>
<!-- 粘贴 -->
<a-doption @click="paste">
<div class="terminal-context-menu-icon">
<icon-paste />
</div>
<div>粘贴</div>
</a-doption>
<!-- 执行 -->
<a-doption @click="exec">
<div class="terminal-context-menu-icon">
<icon-thunderbolt />
</div>
<div>执行</div>
</a-doption>
<!-- 修改 -->
<a-doption @click="exec">
<div class="terminal-context-menu-icon">
<icon-edit />
</div>
<div>修改</div>
</a-doption>
<!-- 删除 -->
<a-doption @click="exec">
<div class="terminal-context-menu-icon">
<icon-delete />
</div>
<div>删除</div>
</a-doption>
<!-- 展开 -->
<a-doption v-if="!item.expand"
@click="() => item.expand = true">
<div class="terminal-context-menu-icon">
<icon-expand />
</div>
<div>展开</div>
</a-doption>
<!-- 收起 -->
<a-doption v-else
@click="() => item.expand = false">
<div class="terminal-context-menu-icon">
<icon-shrink />
</div>
<div>收起</div>
</a-doption>
</template>
</a-dropdown>
</template>
<script lang="ts">
@@ -53,18 +114,44 @@
<script lang="ts" setup>
import type { CommandSnippetQueryResponse } from '@/api/asset/command-snippet';
import { useTerminalStore } from '@/store';
import { useDebounceFn } from '@vueuse/core';
import useCopy from '@/hooks/copy';
const props = defineProps<{
item: CommandSnippetQueryResponse
}>();
const { copy } = useCopy();
const { getCurrentTerminalSession } = useTerminalStore();
// TODO 右键菜单 复制 粘贴 删除 执行 修改
// TODO 修改 删除 拼接有bug
// 展开命令
const expandItem = () => {
props.item.expand = !props.item.expand;
let clickCount = 0;
// 点击命令
const clickItem = () => {
if (++clickCount == 2) {
clickCount = 0;
exec();
} else {
expandItem();
}
};
// 展开
const expandItem = useDebounceFn(() => {
setTimeout(() => {
// 为 0 则代表为双击
if (clickCount !== 0) {
props.item.expand = !props.item.expand;
clickCount = 0;
}
}, 50);
});
// 复制命令
const copyCommand = () => {
copy(props.item.command, false);
};
// 粘贴
@@ -116,6 +203,7 @@
text-overflow: unset;
word-break: break-all;
white-space: unset;
user-select: unset;
}
}
}
@@ -153,6 +241,7 @@
height: 24px;
display: flex;
align-items: center;
user-select: none;
&-name {
width: @item-inline-width;
@@ -173,7 +262,9 @@
text-overflow: ellipsis;
white-space: pre;
width: @item-inline-width;
user-select: none;
}
}
}
</style>

View File

@@ -14,7 +14,7 @@
:disabled="!session.handler.enabledStatus(action.item)"
@click="emits('click', action.item)">
<!-- 图标 -->
<div class="action-icon">
<div class="terminal-context-menu-icon">
<component :is="action.icon" />
</div>
<!-- 文本 -->
@@ -53,9 +53,4 @@
<style lang="less" scoped>
.action-icon {
font-size: 16px;
margin: 0 8px 0 4px;
}
</style>

View File

@@ -68,7 +68,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
// 处理 pong 消息
processPong(payload: OutputPayload): void {
console.log('pong');
// console.log('pong');
}
// 处理输出消息

View File

@@ -1,4 +1,4 @@
import type { TerminalInteractSetting, TerminalShortcutKey } from '@/store/modules/terminal/types';
import type { ShortcutKey, TerminalInteractSetting, TerminalShortcutKey } from '@/store/modules/terminal/types';
import type { ITerminalSession, ITerminalSessionHandler, ITerminalTabManager, TerminalDomRef } from '../types/terminal.type';
import type { Terminal } from 'xterm';
import useCopy from '@/hooks/copy';
@@ -9,6 +9,21 @@ import { saveAs } from 'file-saver';
import { Message } from '@arco-design/web-vue';
import { dateFormat } from '@/utils';
// 组织默认行为的快捷键
const preventKeys: Array<ShortcutKey> = [
{
ctrlKey: true,
altKey: false,
shiftKey: true,
code: 'KeyV'
}, {
ctrlKey: false,
altKey: false,
shiftKey: true,
code: 'Insert'
},
];
const { copy: copyValue, readText } = useCopy();
// 终端会话处理器实现
@@ -37,6 +52,19 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
this.tabManager = tabManager;
}
// 检测是否忽略默认行为
checkPreventDefault(e: KeyboardEvent): boolean {
if (e.type !== 'keydown') {
return false;
}
return !!preventKeys.find(key => {
return key.code === e.code
&& key.altKey === e.altKey
&& key.shiftKey === e.shiftKey
&& key.ctrlKey === e.ctrlKey;
});
}
// 启用状态
enabledStatus(option: string): boolean {
switch (option) {
@@ -187,7 +215,15 @@ export default class TerminalSessionHandler implements ITerminalSessionHandler {
checkAppendMissing(value: string): void {
// 获取最后一行数据
const buffer = this.inst.buffer?.active;
let lastLine = (buffer?.getLine(buffer?.viewportY + buffer?.cursorY)?.translateToString() || '').trimEnd();
let lastLine = '';
if (buffer) {
for (let i = buffer.viewportY + buffer.cursorY; i >= 0; i--) {
lastLine = (buffer.getLine(i)?.translateToString() || '').trimEnd() + lastLine;
if (lastLine.length > value.length) {
break;
}
}
}
// 边界检查
const lastLineLen = lastLine.length;
const spinPartLen = value.length;

View File

@@ -82,7 +82,10 @@ export default class TerminalSession implements ITerminalSession {
private registerShortcut(preference: UnwrapRef<TerminalPreference>) {
// 处理自定义按键
this.inst.attachCustomKeyEventHandler((e: KeyboardEvent) => {
e.preventDefault();
// 检测是否忽略默认行为
if (this.handler.checkPreventDefault(e)) {
e.preventDefault();
}
// 触发快捷键检测
if (e.type === 'keydown'
&& preference.shortcutSetting.enabled

View File

@@ -203,6 +203,8 @@ export interface ITerminalSession {
// 终端会话处理器定义
export interface ITerminalSessionHandler {
// 检测是否忽略默认行为
checkPreventDefault: (e: KeyboardEvent) => boolean;
// 启用状态
enabledStatus: (option: string) => boolean;
// 调用处理方法