支持终端右键菜单

This commit is contained in:
chaos-zhu
2025-04-30 22:51:28 +08:00
parent 8f8e9538cf
commit eb71e6c74a
7 changed files with 155 additions and 21 deletions

View File

@@ -4,16 +4,16 @@
* serv00 SFTP修复
* 连接服务器下拉菜单分组展示
* 新增服务器定时任务功能
* AI对话生成脚本支持一键发送到终端(TODO
## [3.0.6](https://github.com/chaos-zhu/easynode/releases) (2025-04-xx)
## [3.0.6](https://github.com/chaos-zhu/easynode/releases) (2025-05-05)
* AI问答支持历史记录&标题生成
* AI对话生成脚本支持一键发送到终端(TODO
* 新增终端选中多功能菜单(一键询问AI、docker容器ID识别快捷操作. 欢迎issue反馈更多快捷功能)(TODO
* 新增终端选中多功能菜单(一键询问AI、docker容器ID识别快捷操作. 欢迎issue反馈更多快捷功能)
* 添加一些新的内置脚本
* 新增终端链接跳转需按住ctrl|alt键(防止误触)
* 修复粘贴情景下多终端tab同步会话无效bug
* 修复跳板机提示多余空格
* 修复xx情景下多终端tab同步会话bug(TODO
## [3.0.5](https://github.com/chaos-zhu/easynode/releases) (2025-04-04)

View File

@@ -1,6 +1,6 @@
{
"name": "server",
"version": "3.0.5",
"version": "3.0.6",
"description": "easynode-server",
"bin": "./bin/www",
"scripts": {

View File

@@ -1,14 +1,14 @@
[
{
"version": "v3.0.5",
"date": "2025-04-05",
"version": "v3.0.6",
"date": "2025-05-05",
"features": [
"新增AI问答组件及相关配置项,支持deepseekR1、qwq思维链",
"脚本库菜单自定义展示(可在本地设置中配置)",
"支持在面板配置全局API-IP白名单",
"修复终端工具栏高度塌陷",
"修复多页面在内容数量超出视窗时溢出样式问题",
"全局字体优化统一"
"AI问答支持历史记录&标题生成",
"新增终端选中多功能菜单(一键询问AI、docker容器ID识别快捷操作. 欢迎issue反馈更多快捷功能)",
"添加一些新的内置脚本",
"新增终端链接跳转需按住ctrl|alt键(防止误触)",
"修复粘贴情景下多终端tab同步会话无效bug",
"修复跳板机提示多余空格"
]
}
]
]

View File

@@ -1,6 +1,6 @@
{
"name": "web",
"version": "3.0.5",
"version": "3.0.6",
"description": "easynode-web",
"private": true,
"scripts": {

View File

@@ -153,3 +153,12 @@ export const isMobile = () => {
export const handlePlusSupport = () => {
window.open('https://en.221022.xyz/buy-plus', '_blank')
}
export const isDockerId = (id) => {
return /^[a-f0-9]{12}|[a-f0-9]{64}$/.test(id)
}
export const isDockerComposeYml = (str) => {
return /^docker-compose\.yml$/.test(str)
}

View File

@@ -22,9 +22,9 @@
</el-form-item>
<el-form-item>
<div class="form_footer">
<el-button type="primary" :loading="loading" @click="handleUpdate">立即激活</el-button>
<el-button type="success" @click="handlePlusSupport">
购买Plus
<el-button type="success" :loading="loading" @click="handleUpdate">立即激活</el-button>
<el-button type="primary" @click="handlePlusSupport">
获取 Plus Key
<el-icon class="el-icon--right"><TopRight /></el-icon>
</el-button>
<span v-if="!isPlusActive && discount" class="discount_wrapper" @click="handlePlusSupport">

View File

@@ -25,12 +25,12 @@ import socketIo from 'socket.io-client'
import themeList from 'xterm-theme'
import { terminalStatus } from '@/utils/enum'
import { useContextMenu } from '@/composables/useContextMenu'
import { EventBus } from '@/utils'
import { EventBus, isDockerId, isDockerComposeYml } from '@/utils'
const { CONNECTING, CONNECT_SUCCESS, CONNECT_FAIL } = terminalStatus
const { io } = socketIo
const { proxy: { $api, $store, $serviceURI, $notification, $router, $message } } = getCurrentInstance()
const { proxy: { $api, $store, $serviceURI, $notification, $router, $message, $messageBox } } = getCurrentInstance()
const props = defineProps({
hostObj: {
@@ -57,7 +57,6 @@ const timer = ref(null)
const pingTimer = ref(null)
const fitAddon = ref(null)
// const searchBar = ref(null)
const showContextMenu = ref(false)
const socketConnected = ref(false)
const curStatus = ref(CONNECTING)
const terminal = ref(null)
@@ -267,6 +266,7 @@ const shellResize = () => {
let { rows, cols } = term.value
socket.value?.emit('resize', { rows, cols })
}
const onResize = () => {
fitAddon.value = new FitAddon()
term.value.loadAddon(fitAddon.value)
@@ -416,6 +416,22 @@ const handleMouseUp = async (e) => {
}
}
const plusTips = () => {
if (!isPlusActive.value) {
// $message.warning('Plus功能未激活')
$messageBox.confirm('Plus功能未激活', 'Warning', {
confirmButtonText: '前往设置',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
$router.push('/setting?tabKey=plus')
})
return false
}
return true
}
const handleRightClick = async (e) => {
let str = term.value.getSelection().trim()
const sendToAI = str ? {
@@ -449,12 +465,121 @@ const handleRightClick = async (e) => {
handleClear()
}
}
const dockerId = isDockerId(str) ? {
label: 'docker容器ID',
children: [
{
label: '[plus]检测选中内容可能为docker容器ID',
disabled: true
},
{
label: `登录: docker exec -it ${ str } bash`,
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand(`docker exec -it ${ str } bash`)
}
},
{
label: `日志: docker logs -f ${ str }`,
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand(`docker logs -f ${ str }`)
}
},
{
label: `重启: docker restart ${ str }`,
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand(`docker restart ${ str }`)
}
},
{
label: `删除: docker rm -f ${ str }`,
onClick: () => {
if (!plusTips()) return
$messageBox.confirm(`确认删除该容器:${ str }`, 'Warning', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
focusTab()
inputCommand(`docker rm -f ${ str }`)
})
}
},
]
} : null
const dockerComposeYml = isDockerComposeYml(str) ? {
label: 'docker-compose文件',
children: [
{
label: '[plus]检测选中内容可能为docker-compose文件',
disabled: true
},
{
label: '启动: docker-compose up -d',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose up -d')
}
},
{
label: '停止并删除: docker-compose down',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose down')
}
},
{
label: '重启: docker-compose restart',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose restart')
}
},
{
label: '查看日志: docker-compose logs -f',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose logs -f')
}
},
{
label: '拉取新镜像: docker-compose pull',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose pull')
}
},
{
label: '重建: docker-compose up -d --force-recreate',
onClick: () => {
if (!plusTips()) return
focusTab()
inputCommand('docker-compose up -d --force-recreate')
}
},
]
} : null
const menu = [
sendToAI,
copySelection,
paste,
pasteSelection,
clear,
dockerId,
dockerComposeYml,
].filter(Boolean)
showMenu(e, menu)
}