mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 13:18:34 +08:00
feat: 重命名执行模式为编码模式等、修复全屏模式显示问题等
- 统一controller方法,使用{res}/ {res, request}参数,移除非必要的uid参数和package-lock.json文件
- 将"执行模式"改为"编码方式",避免与"脚本执行开关"(手动/自动)产生歧义
- 在脚本编辑器中新增同样的编码方式选择功能
- 修复footerbar传递的执行脚本没有判断useBase64问题
- 修复全屏状态下规则编辑对话框、颜色选择器、以及所有下拉菜单不显示的问题,现在使用Teleport将规则编辑对话框动态挂载到全屏的document.fullscreenElement中,原来的下拉菜单和颜色选择器默认也会用 teleport 挂到 body,需要设置:teleported="false"以禁用
This commit is contained in:
Generated
-11088
File diff suppressed because it is too large
Load Diff
@@ -20,24 +20,14 @@ const DEFAULT_TERMINAL_CONFIG = {
|
||||
const terminalConfigDB = new TerminalConfigDB().getInstance()
|
||||
|
||||
// 获取用户的终端配置
|
||||
async function getTerminalConfig(ctx) {
|
||||
const { uid } = ctx.request.query
|
||||
|
||||
if (!uid || typeof uid !== 'string' || uid.trim() === '') {
|
||||
ctx.body = { success: false, message: '无效的uid参数' }
|
||||
return
|
||||
}
|
||||
|
||||
async function getTerminalConfig({ res }) {
|
||||
try {
|
||||
// 查找用户配置
|
||||
let userConfig = await terminalConfigDB.findOneAsync({ uid, isDefault: { $ne: true } })
|
||||
let userConfig = await terminalConfigDB.findOneAsync({ isDefault: { $ne: true } })
|
||||
|
||||
if (!userConfig) {
|
||||
// 如果没有用户配置,直接返回默认配置
|
||||
userConfig = {
|
||||
...DEFAULT_TERMINAL_CONFIG,
|
||||
uid
|
||||
}
|
||||
userConfig = DEFAULT_TERMINAL_CONFIG
|
||||
} else {
|
||||
// 合并默认配置和用户配置
|
||||
userConfig = {
|
||||
@@ -46,28 +36,22 @@ async function getTerminalConfig(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = { success: true, data: userConfig }
|
||||
res.success({ data: userConfig })
|
||||
} catch (error) {
|
||||
console.error('Error in getTerminalConfig:', error)
|
||||
ctx.body = { success: false, message: '服务器内部错误,请稍后重试' }
|
||||
res.fail({ msg: '服务器内部错误,请稍后重试' })
|
||||
}
|
||||
}
|
||||
|
||||
// 保存用户的终端配置
|
||||
async function saveTerminalConfig(ctx) {
|
||||
const { uid, isDefault, name, _id, createTime, ...configData } = ctx.request.body
|
||||
|
||||
if (!uid || typeof uid !== 'string' || uid.trim() === '') {
|
||||
ctx.body = { success: false, message: '无效的uid参数' }
|
||||
return
|
||||
}
|
||||
async function saveTerminalConfig({ res, request }) {
|
||||
const { isDefault, name, _id, createTime, ...configData } = request.body
|
||||
|
||||
try {
|
||||
const existingConfig = await terminalConfigDB.findOneAsync({ uid, isDefault: { $ne: true } })
|
||||
const existingConfig = await terminalConfigDB.findOneAsync({ isDefault: { $ne: true } })
|
||||
|
||||
const updateData = {
|
||||
...configData,
|
||||
uid,
|
||||
updateTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
@@ -83,10 +67,10 @@ async function saveTerminalConfig(ctx) {
|
||||
await terminalConfigDB.insertAsync(updateData)
|
||||
}
|
||||
|
||||
ctx.body = { success: true, message: '配置保存成功' }
|
||||
res.success({ msg: '配置保存成功' })
|
||||
} catch (error) {
|
||||
console.error('Error in saveTerminalConfig:', error)
|
||||
ctx.body = { success: false, message: '服务器内部错误,请稍后重试' }
|
||||
res.fail({ msg: '服务器内部错误,请稍后重试' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,17 +204,6 @@ module.exports.FileTransferDB = class FileTransferDB {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.HighlightConfigDB = class HighlightConfigDB {
|
||||
constructor() {
|
||||
if (!HighlightConfigDB.instance) {
|
||||
HighlightConfigDB.instance = new Datastore({ filename: terminalConfigDBPath, autoload: true })
|
||||
}
|
||||
}
|
||||
getInstance() {
|
||||
return HighlightConfigDB.instance
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.TerminalConfigDB = class TerminalConfigDB {
|
||||
constructor() {
|
||||
if (!TerminalConfigDB.instance) {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"fs-extra": "^11.2.0",
|
||||
"get-intrinsic": "^1.3.0",
|
||||
"global": "^4.4.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
|
||||
@@ -159,7 +159,7 @@ const useStore = defineStore('global', {
|
||||
},
|
||||
async getTerminalConfig() {
|
||||
try {
|
||||
const { data: terminalConfig } = await $api.getTerminalConfig({ uid: this.uid })
|
||||
const { data: terminalConfig } = await $api.getTerminalConfig()
|
||||
this.$patch({ terminalConfig })
|
||||
} catch (error) {
|
||||
console.error('获取终端配置失败:', error)
|
||||
@@ -185,7 +185,7 @@ const useStore = defineStore('global', {
|
||||
async setTerminalSetting(setTarget = {}) {
|
||||
const newConfig = { ...this.terminalConfig, ...setTarget }
|
||||
try {
|
||||
await $api.saveTerminalConfig({ ...newConfig, uid: this.uid })
|
||||
await $api.saveTerminalConfig(newConfig)
|
||||
this.$patch({ terminalConfig: newConfig })
|
||||
} catch (error) {
|
||||
console.error('保存终端配置失败:', error)
|
||||
|
||||
@@ -68,10 +68,10 @@
|
||||
placeholder="shell script"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="执行模式" prop="useBase64">
|
||||
<el-form-item label="编码方式" prop="useBase64">
|
||||
<el-radio-group v-model="formData.useBase64">
|
||||
<el-radio :value="false">
|
||||
<span>直接执行</span>
|
||||
<span>直接发送</span>
|
||||
<el-tooltip placement="right">
|
||||
<template #content>
|
||||
<div style="max-width: 300px;">
|
||||
@@ -84,12 +84,12 @@
|
||||
</el-tooltip>
|
||||
</el-radio>
|
||||
<el-radio :value="true">
|
||||
<span>Base64编码执行</span>
|
||||
<span>Base64编码</span>
|
||||
<el-tooltip placement="right">
|
||||
<template #content>
|
||||
<div style="max-width: 300px;">
|
||||
适用于多行复杂脚本。<br>
|
||||
脚本通过Base64编码后执行,可以避免:<br>
|
||||
脚本通过Base64编码后发送,可以避免:<br>
|
||||
• 特殊字符转义问题<br>
|
||||
• heredoc标记冲突<br>
|
||||
• 换行符兼容问题<br>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="description" label="描述" />
|
||||
<el-table-column prop="command" label="指令内容" show-overflow-tooltip />
|
||||
<el-table-column prop="useBase64" label="执行模式" width="140px" :formatter="formatExecutionMode" />
|
||||
<el-table-column prop="useBase64" label="编码方式" width="140px" :formatter="formatExecutionMode" />
|
||||
<el-table-column label="操作" fixed="right" width="160px">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.index !== '--'">
|
||||
@@ -239,12 +239,12 @@ const handleEditSuccess = () => {
|
||||
currentScript.value = {}
|
||||
}
|
||||
|
||||
// 格式化执行模式显示
|
||||
// 格式化编码方式显示
|
||||
const formatExecutionMode = (row, column, cellValue) => {
|
||||
if (cellValue === true) {
|
||||
return 'Base64编码执行'
|
||||
return 'Base64编码'
|
||||
}
|
||||
return '直接执行'
|
||||
return '直接发送'
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="编辑高亮规则"
|
||||
width="900px"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleBeforeClose"
|
||||
>
|
||||
<Teleport :to="teleportTarget">
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="编辑高亮规则"
|
||||
width="900px"
|
||||
:append-to-body="false"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleBeforeClose"
|
||||
>
|
||||
<div class="rule-edit">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@@ -30,6 +32,7 @@
|
||||
<el-color-picker
|
||||
v-model="formData.displayColor"
|
||||
size="small"
|
||||
:teleported="false"
|
||||
:predefine="getTextColorPredefines()"
|
||||
@change="handleColorChange"
|
||||
/>
|
||||
@@ -40,6 +43,7 @@
|
||||
v-model="formData.backgroundColor"
|
||||
size="small"
|
||||
show-alpha
|
||||
:teleported="false"
|
||||
:predefine="getBackgroundColorPredefines()"
|
||||
@change="handleBackgroundColorChange"
|
||||
/>
|
||||
@@ -123,7 +127,8 @@
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -132,6 +137,11 @@ import { HIGHLIGHT_RULES, TerminalHighlighter } from '@/utils/highlighter'
|
||||
|
||||
const { proxy: { $message, $messageBox } } = getCurrentInstance()
|
||||
|
||||
// 全屏时teleport到fullscreenElement,否则teleport到body
|
||||
const teleportTarget = computed(() => {
|
||||
return document.fullscreenElement || 'body'
|
||||
})
|
||||
|
||||
// 检查颜色是否已被使用
|
||||
const isColorUsed = (color, rules, excludeKey = null) => {
|
||||
const usedColors = Object.entries(rules)
|
||||
|
||||
@@ -92,6 +92,27 @@
|
||||
:rows="10"
|
||||
placeholder="请输入脚本内容..."
|
||||
/>
|
||||
|
||||
<!-- 脚本编辑器中的脚本编码模式选项 -->
|
||||
<div class="execution_mode_selector">
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<div style="max-width: 300px;">
|
||||
<strong>直接发送:</strong>适用于单行简单脚本,内容直接发送到终端<br><br>
|
||||
<strong>Base64编码:</strong>适用于多行复杂脚本,通过Base64编码后发送,可避免特殊字符转义、heredoc标记冲突等问题
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
v-model="editorUseBase64"
|
||||
size="small"
|
||||
style="width: 100px;"
|
||||
:teleported="false"
|
||||
>
|
||||
<el-option :value="false" label="直接发送" />
|
||||
<el-option :value="true" label="Base64编码" />
|
||||
</el-select>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="action_btn">
|
||||
<el-dropdown
|
||||
split-button
|
||||
@@ -99,6 +120,7 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!scriptContent"
|
||||
:teleported="false"
|
||||
@click="handleSendToTerminal"
|
||||
>
|
||||
<template #dropdown>
|
||||
@@ -155,6 +177,7 @@ const scriptEditVisible = ref(false)
|
||||
const editScriptData = ref(null)
|
||||
const scriptGroupVisible = ref(false)
|
||||
const isClearContent = ref(false)
|
||||
const editorUseBase64 = ref(false)
|
||||
|
||||
const isPlusActive = computed(() => $store.isPlusActive)
|
||||
const scriptGroupList = computed(() => $store.scriptGroupList || [])
|
||||
@@ -175,7 +198,7 @@ const handleSelectGroup = (group) => {
|
||||
|
||||
const handleExecScript = (script) => {
|
||||
if (!isPlusActive.value) return $message.warning('此功能仅限PLUS版使用')
|
||||
emit('exec-command', script.command)
|
||||
emit('exec-command', { command: script.command, useBase64: script.useBase64 || false })
|
||||
}
|
||||
|
||||
const handleEditScript = (script) => {
|
||||
@@ -191,7 +214,7 @@ const handleSendToEditor = (script) => {
|
||||
|
||||
const handleSendToTerminal = () => {
|
||||
if (!isPlusActive.value) return $message.warning('此功能仅限PLUS版使用')
|
||||
emit('exec-command', scriptContent.value)
|
||||
emit('exec-command', { command: scriptContent.value, useBase64: editorUseBase64.value })
|
||||
if (isClearContent.value) {
|
||||
scriptContent.value = ''
|
||||
}
|
||||
@@ -490,6 +513,15 @@ if (scriptGroupList.value.length) {
|
||||
}
|
||||
}
|
||||
|
||||
.execution_mode_selector {
|
||||
position: absolute;
|
||||
right: 140px;
|
||||
bottom: 20px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.action_btn {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
:show-message="false"
|
||||
>
|
||||
<el-form-item label="终端主题" prop="theme">
|
||||
<el-select v-model="theme" placeholder="" style="width: 100%;">
|
||||
<el-select v-model="theme" placeholder="" style="width: 100%;" :teleported="false">
|
||||
<el-option
|
||||
v-for="(value, key) in themeList"
|
||||
:key="key"
|
||||
@@ -26,7 +26,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="终端字体" prop="fontFamily">
|
||||
<el-select v-model="fontFamily" placeholder="选择字体" style="width: 100%;">
|
||||
<el-select v-model="fontFamily" placeholder="选择字体" style="width: 100%;" :teleported="false">
|
||||
<el-option
|
||||
v-for="font in commonFonts"
|
||||
:key="font.value"
|
||||
@@ -47,6 +47,7 @@
|
||||
<el-color-picker
|
||||
v-model="customFontColor"
|
||||
show-alpha
|
||||
:teleported="false"
|
||||
:predefine="predefineColors"
|
||||
@change="handleColorChange"
|
||||
/>
|
||||
@@ -62,6 +63,7 @@
|
||||
<el-color-picker
|
||||
v-model="customCursorColor"
|
||||
show-alpha
|
||||
:teleported="false"
|
||||
:predefine="predefineColors"
|
||||
@change="handleCursorColorChange"
|
||||
/>
|
||||
@@ -74,6 +76,7 @@
|
||||
<el-color-picker
|
||||
v-model="selectionColor"
|
||||
show-alpha
|
||||
:teleported="false"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -789,20 +789,26 @@ const handleLinkHost = (hostDescObj) => {
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// scriptDescObj: 脚本库对象或脚本命令
|
||||
// scriptDescObj: 脚本库对象、脚本命令或者包含command和useBase64的对象
|
||||
const handleExecScript = async (scriptDescObj) => {
|
||||
if (!scriptDescObj) return // clearCheckedNodes二次触发change事件
|
||||
let command = ''
|
||||
let useBase64 = false
|
||||
|
||||
const id = Array.isArray(scriptDescObj) ? scriptDescObj.slice(-1)[0] : scriptDescObj?.id
|
||||
if (id) {
|
||||
const script = scriptList.value.find((item) => item.id === id)
|
||||
command = script?.command
|
||||
useBase64 = script?.useBase64 || false // 读取脚本的base64配置
|
||||
// 处理从footerbar传来的对象格式{command,useBase64}
|
||||
if (scriptDescObj.command !== undefined) {
|
||||
command = scriptDescObj.command
|
||||
useBase64 = scriptDescObj.useBase64 || false
|
||||
} else {
|
||||
command = scriptDescObj
|
||||
useBase64 = false // 外部传入的命令默认不用base64
|
||||
const id = Array.isArray(scriptDescObj) ? scriptDescObj.slice(-1)[0] : scriptDescObj?.id
|
||||
if (id) {
|
||||
const script = scriptList.value.find((item) => item.id === id)
|
||||
command = script?.command
|
||||
useBase64 = script?.useBase64 || false // 读取脚本的base64配置
|
||||
} else {
|
||||
command = scriptDescObj
|
||||
useBase64 = false // 外部传入的命令默认不用base64
|
||||
}
|
||||
}
|
||||
if (!command) return $message.warning('未找到对应的脚本')
|
||||
|
||||
|
||||
@@ -768,7 +768,7 @@ const inputCommand = (command, type = 'input', useBase64 = false) => {
|
||||
const encodedScript = btoa(String.fromCharCode(...utf8Bytes))
|
||||
command = `echo '${ encodedScript }' | base64 -d | bash${ autoExecuteScript.value ? '\n' : '' }`
|
||||
} else {
|
||||
// 直接执行模式:根据配置添加换行符
|
||||
// 直接发送模式:根据脚本执行模式添加换行符
|
||||
command = command + (autoExecuteScript.value ? '\n' : '')
|
||||
}
|
||||
}
|
||||
@@ -788,7 +788,7 @@ const execExternalCommand = (command, useBase64 = false) => {
|
||||
const encodedScript = btoa(String.fromCharCode(...utf8Bytes))
|
||||
command = `echo '${ encodedScript }' | base64 -d | bash${ autoExecuteScript.value ? '\n' : '' }`
|
||||
} else {
|
||||
// 直接执行模式:根据配置添加换行符
|
||||
// 直接发送模式:根据脚本执行模式添加换行符
|
||||
command = command + (autoExecuteScript.value ? '\n' : '')
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user