双击终端会话 tab 复制.

This commit is contained in:
lijiahang
2024-03-08 19:36:13 +08:00
parent 54ae18987c
commit f4c5517f9f
11 changed files with 94 additions and 40 deletions

View File

@@ -6,9 +6,12 @@
🐞 修复 SFTP 加载失败后一直 loading
🐞 修复 SSH 配置未启用还可以连接
🐞 修复 主机配置保存后无法修改状态
🐞 修复 主机配置保存后无法修改状态
🐞 修复 添加快捷命令时编辑器无代码提示
🔨 修改 菜单路由命名逻辑修改
🔨 优化 前端组件命名规范化
🔨 优化 前端组件命名规范化
🌈 新增 双击终端会话 Tab 快速复制
🌈 新增 执行模板功能
[如何升级](/about/update.md?id=_v102)

View File

@@ -37,3 +37,38 @@
> SFTP
* 预览: 默认只能预览 2MB 以内的普通文件, 这个大小可以在前端 env 文件中修改 `VITE_SFTP_PREVIEW_MB`
> 终端面板
⭐ 双击终端标签可快速复制会话
### 执行模板
用来维护批量执行的命令模板, 支持动态参数, 使用 `@{{ xxx }}` 来替换命令参数。
* 新增: 新增执行模板
* 执行: 使用此命令模板批量执行主机命令
* 修改: 修改执行模板
* 删除: 删除执行模板
> 内置参数
| 参数 | 描述 |
|:----------------|:-------------------------|
| hostId | 执行主机id |
| hostName | 执行主机名称 |
| hostCode | 执行主机编码 |
| hostAddress | 执行主机地址 |
| userId | 执行用户id |
| username | 执行用户名 |
| execId | 执行记录id |
| execHostId | 执行主机记录id |
| uuid | 生成任务维度 uuid |
| uuidShort | 生成任务维度 uuid 无 '-' |
| hostUuid | 生成机器维度 uuid |
| hostUuidShort | 生成机器维度 uuid 无 '-' |
| timestampMillis | 时间戳毫秒 |
| timestamp | 时间戳 |
| date | 执行时间 yyyy-MM-dd |
| datetime | 执行时间 yyyy-MM-dd HH:mm:ss |

View File

@@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
#end
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@@ -154,6 +155,7 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
// 转换
return list.stream()
.map(${type}Convert.MAPPER::to)
.sorted(Comparator.comparing(${type}VO::getId).reversed())
.collect(Collectors.toList());
}
@@ -276,7 +278,8 @@ public class ${table.serviceImplName} implements ${table.serviceName} {
#foreach($field in ${table.fields})
.eq(${type}DO::get${field.capitalName}, searchValue)#if($foreach.hasNext).or()#end
#end
);
)
.orderByDesc(${type}DO::getId);
}
}

View File

@@ -134,8 +134,6 @@
const emits = defineEmits(['openAdd', 'openUpdate']);
const list = ref<${vue.featureEntity}QueryResponse[]>([]);
const cardColLayout = useColLayout();
const pagination = usePagination();
const { loading, setLoading } = useLoading();
@@ -143,6 +141,7 @@
const { toOptions, getDictValue } = useDictStore();
#end
const list = ref<${vue.featureEntity}QueryResponse[]>([]);
const formRef = ref();
const formModel = reactive<${vue.featureEntity}QueryRequest>({
searchValue: undefined,

View File

@@ -70,6 +70,8 @@
import { useDictStore } from '@/store';
#end
const emits = defineEmits(['added', 'updated']);
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
#if($dictMap.entrySet().size() > 0)
@@ -78,6 +80,8 @@
const title = ref<string>();
const isAddHandle = ref<boolean>(true);
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const defaultForm = (): ${vue.featureEntity}UpdateRequest => {
return {
@@ -87,11 +91,6 @@
};
};
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const emits = defineEmits(['added', 'updated']);
// 打开新增
const openAdd = () => {
title.value = '添加${table.comment}';

View File

@@ -74,6 +74,8 @@
import { useDictStore } from '@/store';
#end
const emits = defineEmits(['added', 'updated']);
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
#if($dictMap.entrySet().size() > 0)
@@ -82,6 +84,8 @@
const title = ref<string>();
const isAddHandle = ref<boolean>(true);
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const defaultForm = (): ${vue.featureEntity}UpdateRequest => {
return {
@@ -91,11 +95,6 @@
};
};
const formRef = ref<any>();
const formModel = ref<${vue.featureEntity}UpdateRequest>({});
const emits = defineEmits(['added', 'updated']);
// 打开新增
const openAdd = () => {
title.value = '添加${table.comment}';

View File

@@ -157,11 +157,6 @@
const emits = defineEmits(['openAdd', 'openUpdate']);
#if($vue.enableRowSelection)
const selectedKeys = ref<number[]>([]);
#end
const tableRenderData = ref<${vue.featureEntity}QueryResponse[]>([]);
const pagination = usePagination();
#if($vue.enableRowSelection)
const rowSelection = useRowSelection();
@@ -171,6 +166,10 @@
const { toOptions, getDictValue } = useDictStore();
#end
#if($vue.enableRowSelection)
const selectedKeys = ref<number[]>([]);
#end
const tableRenderData = ref<${vue.featureEntity}QueryResponse[]>([]);
const formModel = reactive<${vue.featureEntity}QueryRequest>({
#foreach($field in ${table.fields})
${field.propertyName}: undefined,

View File

@@ -62,6 +62,13 @@
#else
import ${vue.featureEntity}FormModal from './components/${vue.feature}-form-modal.vue';
#end
#if($vue.enableCardView)
const appStore = useAppStore();
// FIXME 这里需要修改一下字段名称 并且在 appStore 定义该字段
const renderTable = computed(() => appStore.${vue.featureEntityFirstLower}View === 'table');
#end
const render = ref(false);
const table = ref();
@@ -73,12 +80,6 @@
#else
const modal = ref();
#end
#if($vue.enableCardView)
const appStore = useAppStore();
// FIXME 这里需要修改一下字段名称 并且在 appStore 定义该字段
const renderTable = computed(() => appStore.${vue.featureEntityFirstLower}View === 'table');
#end
// 添加回调
const modalAddCallback = () => {

View File

@@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@@ -99,6 +100,7 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
// 转换
return list.stream()
.map(ExecTemplateConvert.MAPPER::to)
.sorted(Comparator.comparing(ExecTemplateVO::getId).reversed())
.collect(Collectors.toList());
}
@@ -153,7 +155,8 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
return execTemplateDAO.wrapper()
.eq(ExecTemplateDO::getId, request.getId())
.like(ExecTemplateDO::getName, request.getName())
.like(ExecTemplateDO::getCommand, request.getCommand());
.like(ExecTemplateDO::getCommand, request.getCommand())
.orderByDesc(ExecTemplateDO::getId);
}
}

View File

@@ -9,39 +9,51 @@ export interface TemplateParam {
export const builtinsParams: Array<TemplateParam> = [
{
name: 'hostId',
desc: '主机id'
desc: '执行主机id'
}, {
name: 'hostName',
desc: '主机名称'
desc: '执行主机名称'
}, {
name: 'hostCode',
desc: '主机编码'
desc: '执行主机编码'
}, {
name: 'hostAddress',
desc: '执行主机地址'
}, {
name: 'userId',
desc: '执行用户id'
}, {
name: 'username',
desc: '执行用户名'
desc: '执行用户名'
}, {
name: 'execId',
desc: '执行id'
desc: '执行记录id'
}, {
name: 'execHostId',
desc: '执行主机记录id'
}, {
name: 'uuid',
desc: 'uuid'
desc: '生成任务维度 uuid'
}, {
name: 'uuidShort',
desc: 'uuid 无 \'-\''
desc: '生成任务维度 uuid 无 \'-\''
}, {
name: 'timeMillis',
name: 'hostUuid',
desc: '生成机器维度 uuid'
}, {
name: 'hostUuidShort',
desc: '生成机器维度 uuid 无 \'-\''
}, {
name: 'timestampMillis',
desc: '时间戳毫秒'
}, {
name: 'timestamp',
desc: '时间戳'
}, {
name: 'date',
desc: '时间 yyyy-MM-dd'
desc: '执行时间 yyyy-MM-dd'
}, {
name: 'datetime',
desc: '时间 yyyy-MM-dd HH:mm:ss'
desc: '执行时间 yyyy-MM-dd HH:mm:ss'
},
];

View File

@@ -21,8 +21,9 @@
:key="tab.key">
<!-- 标题 -->
<template #title>
<span class="tab-title-wrapper"
:style="{ 'border-bottom': `2px ${tab.color || 'transparent'} solid` }">
<span class="tab-title-wrapper usn"
:style="{ 'border-bottom': `2px ${tab.color || 'transparent'} solid` }"
@dblclick="copySession(tab, index)">
<span class="tab-title-icon">
<component :is="tab.icon" />
</span>
@@ -59,7 +60,7 @@
const emits = defineEmits(['close', 'openNewConnect']);
const { sessionManager } = useTerminalStore();
const { sessionManager, copySession } = useTerminalStore();
// 监听 tab 切换
watch(() => props.panel.active, (active, before) => {