✨ 终端支持快捷设置
This commit is contained in:
parent
70e867410f
commit
53cc1628c2
@ -2,7 +2,7 @@ import { io } from 'socket.io-client'
|
|||||||
import { defineStore, acceptHMRUpdate } from 'pinia'
|
import { defineStore, acceptHMRUpdate } from 'pinia'
|
||||||
import $api from '@/api'
|
import $api from '@/api'
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
// import ping from '@/utils/ping'
|
import { isHttps } from '@/utils'
|
||||||
|
|
||||||
const { defaultClientPort } = config
|
const { defaultClientPort } = config
|
||||||
|
|
||||||
@ -34,9 +34,12 @@ const useStore = defineStore({
|
|||||||
],
|
],
|
||||||
terminalConfig: {
|
terminalConfig: {
|
||||||
...{
|
...{
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
themeName: localStorage.getItem('themeName') || 'Afterglow',
|
themeName: 'Afterglow',
|
||||||
background: localStorage.getItem('terminalBackground') || ''
|
background: '',
|
||||||
|
quickCopy: isHttps(),
|
||||||
|
quickPaste: isHttps(),
|
||||||
|
autoExecuteScript: false
|
||||||
},
|
},
|
||||||
...(localStorage.getItem('terminalConfig') ? JSON.parse(localStorage.getItem('terminalConfig')) : {})
|
...(localStorage.getItem('terminalConfig') ? JSON.parse(localStorage.getItem('terminalConfig')) : {})
|
||||||
}
|
}
|
||||||
|
@ -56,10 +56,10 @@ export const sortString = (arr = []) => {
|
|||||||
let c1 = ''
|
let c1 = ''
|
||||||
let c2 = ''
|
let c2 = ''
|
||||||
let temp = a.length > b.length ? b : a
|
let temp = a.length > b.length ? b : a
|
||||||
for(let i = 0; i < temp.length; i++) {
|
for (let i = 0; i < temp.length; i++) {
|
||||||
c1 = a[i].toLowerCase()
|
c1 = a[i].toLowerCase()
|
||||||
c2 = b[i].toLowerCase()
|
c2 = b[i].toLowerCase()
|
||||||
if(c1 !== c2) break
|
if (c1 !== c2) break
|
||||||
}
|
}
|
||||||
return c1.charCodeAt() - c2.charCodeAt()
|
return c1.charCodeAt() - c2.charCodeAt()
|
||||||
})
|
})
|
||||||
@ -83,10 +83,10 @@ export const sortDirTree = (tree = []) => {
|
|||||||
let c1 = ''
|
let c1 = ''
|
||||||
let c2 = ''
|
let c2 = ''
|
||||||
let temp = aName.length > bName.length ? bName : aName
|
let temp = aName.length > bName.length ? bName : aName
|
||||||
for(let i = 0; i < temp.length; i++) {
|
for (let i = 0; i < temp.length; i++) {
|
||||||
c1 = aName[i].toLowerCase()
|
c1 = aName[i].toLowerCase()
|
||||||
c2 = bName[i].toLowerCase()
|
c2 = bName[i].toLowerCase()
|
||||||
if(c1 !== c2) break
|
if (c1 !== c2) break
|
||||||
}
|
}
|
||||||
return c1.charCodeAt() - c2.charCodeAt()
|
return c1.charCodeAt() - c2.charCodeAt()
|
||||||
})
|
})
|
||||||
@ -128,3 +128,7 @@ export const exportFile = (data, filename, mimeType = 'application/json') =>{
|
|||||||
document.body.removeChild(link)
|
document.body.removeChild(link)
|
||||||
window.URL.revokeObjectURL(url)
|
window.URL.revokeObjectURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isHttps = () => {
|
||||||
|
return window.location.protocol === 'https:'
|
||||||
|
}
|
||||||
|
@ -4,20 +4,13 @@
|
|||||||
<el-tab-pane label="修改密码" lazy>
|
<el-tab-pane label="修改密码" lazy>
|
||||||
<User />
|
<User />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<!-- <el-tab-pane label="分组管理">
|
|
||||||
<Group />
|
|
||||||
</el-tab-pane> -->
|
|
||||||
<el-tab-pane label="登录日志">
|
<el-tab-pane label="登录日志">
|
||||||
<Record />
|
<Record />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<!-- <el-tab-pane label="实例排序" lazy>
|
|
||||||
<Sort @update-list="emitUpdateList" />
|
|
||||||
</el-tab-pane> -->
|
|
||||||
<el-tab-pane label="全局通知" lazy>
|
<el-tab-pane label="全局通知" lazy>
|
||||||
<GlobalNotify />
|
<GlobalNotify />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="通知配置" lazy>
|
<el-tab-pane label="通知配置" lazy>
|
||||||
<!-- <EmailList /> -->
|
|
||||||
<NotifyConfig />
|
<NotifyConfig />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
@ -3,59 +3,120 @@
|
|||||||
v-model="visible"
|
v-model="visible"
|
||||||
width="600px"
|
width="600px"
|
||||||
top="120px"
|
top="120px"
|
||||||
title="终端设置"
|
title="本地设置"
|
||||||
:append-to-body="false"
|
:append-to-body="false"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-tabs tab-position="top">
|
||||||
ref="formRef"
|
<el-tab-pane label="终端设置" lazy>
|
||||||
label-suffix=":"
|
<el-form
|
||||||
label-width="60px"
|
ref="formRef"
|
||||||
:show-message="false"
|
label-suffix=":"
|
||||||
>
|
label-width="100px"
|
||||||
<el-form-item label="主题" prop="theme">
|
:show-message="false"
|
||||||
<el-select v-model="theme" placeholder="" style="width: 100%;">
|
>
|
||||||
<el-option
|
<el-form-item label="终端主题" prop="theme">
|
||||||
v-for="(value, key) in themeList"
|
<el-select v-model="theme" placeholder="" style="width: 100%;">
|
||||||
:key="key"
|
<el-option
|
||||||
:label="key"
|
v-for="(value, key) in themeList"
|
||||||
:value="key"
|
:key="key"
|
||||||
/>
|
:label="key"
|
||||||
</el-select>
|
:value="key"
|
||||||
</el-form-item>
|
/>
|
||||||
<el-form-item label="背景" prop="backgroundImage">
|
</el-select>
|
||||||
<ul class="background_list">
|
</el-form-item>
|
||||||
<li :class="background ? '' : 'active'" @click="changeBackground('')">
|
<el-form-item label="终端字体" prop="fontSize">
|
||||||
<el-image class="image">
|
<el-input-number v-model="fontSize" :min="12" :max="30" />
|
||||||
<template #error>
|
</el-form-item>
|
||||||
<div class="theme_background_text">
|
<el-form-item label="终端背景" prop="backgroundImage">
|
||||||
主题背景
|
<ul class="background_list">
|
||||||
</div>
|
<li :class="background ? '' : 'active'" @click="changeBackground('')">
|
||||||
</template>
|
<el-image class="image">
|
||||||
</el-image>
|
<template #error>
|
||||||
</li>
|
<div class="theme_background_text">
|
||||||
<li
|
主题背景
|
||||||
v-for="url in defaultBackgroundImages"
|
</div>
|
||||||
:key="url"
|
</template>
|
||||||
:class="background === url ? 'active' : ''"
|
</el-image>
|
||||||
@click="changeBackground(url)"
|
</li>
|
||||||
>
|
<li
|
||||||
<el-image class="image" :src="url" />
|
v-for="url in defaultBackgroundImages"
|
||||||
</li>
|
:key="url"
|
||||||
</ul>
|
:class="background === url ? 'active' : ''"
|
||||||
<div class="custom_background">
|
@click="changeBackground(url)"
|
||||||
<el-input
|
>
|
||||||
v-model="backgroundUrl"
|
<el-image class="image" :src="url" />
|
||||||
clearable
|
</li>
|
||||||
placeholder="自定义背景图片url"
|
</ul>
|
||||||
autocomplete="on"
|
<div class="custom_background">
|
||||||
/>
|
<el-input
|
||||||
</div>
|
v-model="background"
|
||||||
</el-form-item>
|
clearable
|
||||||
<el-form-item label="字体" prop="fontSize">
|
placeholder="自定义背景图片url"
|
||||||
<el-input-number v-model="fontSize" :min="12" :max="30" />
|
autocomplete="on"
|
||||||
</el-form-item>
|
/>
|
||||||
</el-form>
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="快捷操作">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
label-suffix=":"
|
||||||
|
label-width="100px"
|
||||||
|
:show-message="false"
|
||||||
|
>
|
||||||
|
<el-form-item label="选中复制" prop="quickCopy">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
content="启用后选中文本自动复制到剪贴板(需https支持)"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="quickCopy"
|
||||||
|
class="swtich"
|
||||||
|
inline-prompt
|
||||||
|
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
/>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="快捷粘贴" prop="quickPaste">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
content="启用后右键粘贴剪贴板内容(需https支持)"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="quickPaste"
|
||||||
|
class="swtich"
|
||||||
|
inline-prompt
|
||||||
|
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||||
|
active-text="开启"
|
||||||
|
inactive-text="关闭"
|
||||||
|
/>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="脚本执行" prop="autoExecuteScript">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
content="启用后从脚本库选中脚本后自动执行(回车操作)"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="autoExecuteScript"
|
||||||
|
class="swtich"
|
||||||
|
inline-prompt
|
||||||
|
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||||
|
active-text="自动"
|
||||||
|
inactive-text="手动"
|
||||||
|
/>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog_footer">
|
<span class="dialog_footer">
|
||||||
<el-button @click="visible = false">关闭</el-button>
|
<el-button @click="visible = false">关闭</el-button>
|
||||||
@ -95,6 +156,18 @@ const fontSize = computed({
|
|||||||
get: () => $store.terminalConfig.fontSize,
|
get: () => $store.terminalConfig.fontSize,
|
||||||
set: (newVal) => $store.setTerminalSetting({ fontSize: newVal })
|
set: (newVal) => $store.setTerminalSetting({ fontSize: newVal })
|
||||||
})
|
})
|
||||||
|
const quickCopy = computed({
|
||||||
|
get: () => $store.terminalConfig.quickCopy,
|
||||||
|
set: (newVal) => $store.setTerminalSetting({ quickCopy: newVal })
|
||||||
|
})
|
||||||
|
const quickPaste = computed({
|
||||||
|
get: () => $store.terminalConfig.quickPaste,
|
||||||
|
set: (newVal) => $store.setTerminalSetting({ quickPaste: newVal })
|
||||||
|
})
|
||||||
|
const autoExecuteScript = computed({
|
||||||
|
get: () => $store.terminalConfig.autoExecuteScript,
|
||||||
|
set: (newVal) => $store.setTerminalSetting({ autoExecuteScript: newVal })
|
||||||
|
})
|
||||||
|
|
||||||
const changeBackground = (url) => {
|
const changeBackground = (url) => {
|
||||||
background.value = url || ''
|
background.value = url || ''
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div
|
<div
|
||||||
ref="terminalRef"
|
ref="terminalRef"
|
||||||
class="terminal_container"
|
class="terminal_container"
|
||||||
@contextmenu.prevent="handleRightClick"
|
@contextmenu="handleRightClick"
|
||||||
/>
|
/>
|
||||||
<!-- <div class="terminal_command_history">
|
<!-- <div class="terminal_command_history">
|
||||||
<CommandHistory :list="commandHistoryList" />
|
<CommandHistory :list="commandHistoryList" />
|
||||||
@ -60,7 +60,10 @@ const background = computed(() => $store.terminalConfig.background)
|
|||||||
const hostObj = computed(() => props.hostObj)
|
const hostObj = computed(() => props.hostObj)
|
||||||
const hostId = computed(() => hostObj.value.id)
|
const hostId = computed(() => hostObj.value.id)
|
||||||
const host = computed(() => hostObj.value.host)
|
const host = computed(() => hostObj.value.host)
|
||||||
let menuCollapse = computed(() => $store.menuCollapse)
|
const menuCollapse = computed(() => $store.menuCollapse)
|
||||||
|
const quickCopy = computed(() => $store.terminalConfig.quickCopy)
|
||||||
|
const quickPaste = computed(() => $store.terminalConfig.quickPaste)
|
||||||
|
const autoExecuteScript = computed(() => $store.terminalConfig.autoExecuteScript)
|
||||||
|
|
||||||
watch(menuCollapse, () => {
|
watch(menuCollapse, () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@ -272,6 +275,7 @@ const onFindText = () => {
|
|||||||
|
|
||||||
const onSelectionChange = () => {
|
const onSelectionChange = () => {
|
||||||
term.value.onSelectionChange(() => {
|
term.value.onSelectionChange(() => {
|
||||||
|
if (!quickCopy.value) return
|
||||||
let str = term.value.getSelection()
|
let str = term.value.getSelection()
|
||||||
if (!str) return
|
if (!str) return
|
||||||
const text = new Blob([str,], { type: 'text/plain' })
|
const text = new Blob([str,], { type: 'text/plain' })
|
||||||
@ -323,8 +327,9 @@ const onData = () => {
|
|||||||
term.value.onData((key) => {
|
term.value.onData((key) => {
|
||||||
if (socketConnected.value === false) return
|
if (socketConnected.value === false) return
|
||||||
let acsiiCode = key.codePointAt()
|
let acsiiCode = key.codePointAt()
|
||||||
if (acsiiCode === 22) return handlePaste()
|
// console.log(acsiiCode)
|
||||||
if (acsiiCode === 6) return searchBar.value.show()
|
if (acsiiCode === 22) return handlePaste() // Ctrl + V
|
||||||
|
// if (acsiiCode === 6) return searchBar.value.show() // Ctrl + F
|
||||||
enterTimer.value = setTimeout(() => {
|
enterTimer.value = setTimeout(() => {
|
||||||
if (enterTimer.value) clearTimeout(enterTimer.value)
|
if (enterTimer.value) clearTimeout(enterTimer.value)
|
||||||
if (key === '\r') { // Enter
|
if (key === '\r') { // Enter
|
||||||
@ -363,7 +368,9 @@ const onData = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRightClick = async () => {
|
const handleRightClick = async (e) => {
|
||||||
|
if (!quickPaste.value) return
|
||||||
|
e.preventDefault()
|
||||||
try {
|
try {
|
||||||
const clipboardText = await navigator.clipboard.readText()
|
const clipboardText = await navigator.clipboard.readText()
|
||||||
if (!clipboardText) return
|
if (!clipboardText) return
|
||||||
@ -384,6 +391,7 @@ const handleClear = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handlePaste = async () => {
|
const handlePaste = async () => {
|
||||||
|
if (!quickPaste.value) return
|
||||||
let key = await navigator.clipboard.readText()
|
let key = await navigator.clipboard.readText()
|
||||||
emit('inputCommand', key)
|
emit('inputCommand', key)
|
||||||
socket.value.emit('input', key)
|
socket.value.emit('input', key)
|
||||||
@ -398,6 +406,7 @@ const focusTab = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inputCommand = (command) => {
|
const inputCommand = (command) => {
|
||||||
|
command = command + (autoExecuteScript.value ? '\n' : '')
|
||||||
socket.value.emit('input', command)
|
socket.value.emit('input', command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,14 +47,14 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dropdown> -->
|
</el-dropdown> -->
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="link_text">设置<el-icon><arrow-down /></el-icon></span>
|
<span class="link_text">首选项<el-icon><arrow-down /></el-icon></span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="handleFullScreen">
|
<el-dropdown-item @click="handleFullScreen">
|
||||||
<span>启用全屏</span>
|
<span>启用全屏</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="showSetting = true">
|
<el-dropdown-item @click="showSetting = true">
|
||||||
<span>终端设置</span>
|
<span>本地设置</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
@ -247,7 +247,6 @@ const handleCloseAllTab = () => {
|
|||||||
|
|
||||||
const handleExecScript = (scriptObj) => {
|
const handleExecScript = (scriptObj) => {
|
||||||
let { command } = scriptObj
|
let { command } = scriptObj
|
||||||
command += '\n'
|
|
||||||
if (!isSyncAllSession.value) return handleInputCommand(command)
|
if (!isSyncAllSession.value) return handleInputCommand(command)
|
||||||
terminalRefs.value.forEach(terminalRef => {
|
terminalRefs.value.forEach(terminalRef => {
|
||||||
terminalRef.inputCommand(command)
|
terminalRef.inputCommand(command)
|
||||||
@ -355,7 +354,7 @@ const handleInputCommand = async (command) => {
|
|||||||
const curTerminalRef = terminalRefs.value[activeTabIndex.value]
|
const curTerminalRef = terminalRefs.value[activeTabIndex.value]
|
||||||
await $nextTick()
|
await $nextTick()
|
||||||
curTerminalRef?.focusTab()
|
curTerminalRef?.focusTab()
|
||||||
curTerminalRef.inputCommand(`${ command }`) // \n
|
curTerminalRef.inputCommand(`${ command }`)
|
||||||
showInputCommand.value = false
|
showInputCommand.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user