feat: improve the OpenClaw terminal (#12230)

This commit is contained in:
CityFun
2026-03-19 18:26:37 +08:00
committed by GitHub
parent 11d78644ee
commit 9f61aac8be
2 changed files with 118 additions and 3 deletions
@@ -0,0 +1,110 @@
<template>
<DrawerPro
v-model="terminalVisible"
:header="$t('menu.terminal')"
@close="handleClose"
:resource="title"
fullScreen
:size="globalStore.isFullScreen ? 'full' : 'large'"
:autoClose="false"
>
<template #content>
<el-form label-position="top" @submit.prevent>
<el-form-item :label="$t('commons.table.user')" prop="user">
<el-select v-model="form.user" :disabled="form.users.length <= 1" @change="reConnect">
<el-option v-for="item in form.users" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
<Terminal class="terminal" ref="terminalRef" v-if="terminalOpen"></Terminal>
</template>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="handleClose">
{{ $t('commons.button.disConn') }}
</el-button>
</span>
</template>
</DrawerPro>
</template>
<script lang="ts" setup>
import { nextTick, reactive, ref } from 'vue';
import Terminal from '@/components/terminal/index.vue';
import { useGlobalStore } from '@/composables/useGlobalStore';
const { globalStore, currentNode } = useGlobalStore();
const title = ref('');
const terminalVisible = ref(false);
const terminalOpen = ref(false);
const terminalRef = ref<InstanceType<typeof Terminal> | null>(null);
const form = reactive({
containerID: '',
users: [] as string[],
user: '',
shell: '',
node: '',
});
interface DialogProps {
containerID: string;
title: string;
users: string[];
shell: string;
node?: string;
}
const acceptParams = async (params: DialogProps): Promise<void> => {
terminalVisible.value = true;
form.containerID = params.containerID;
form.users = [...params.users];
form.user = form.users[0];
form.shell = params.shell;
form.node = params.node || currentNode.value;
title.value = params.title;
await reConnect();
};
const initTerm = async () => {
terminalOpen.value = true;
await nextTick();
let args = `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.shell}`;
if (form.node) {
args += `&operateNode=${form.node}`;
}
terminalRef.value?.acceptParams({
endpoint: '/api/v2/containers/exec',
args,
error: '',
initCmd: '',
});
};
const reConnect = async () => {
terminalRef.value?.onClose();
terminalOpen.value = false;
await nextTick();
await initTerm();
};
const onClose = () => {
terminalRef.value?.onClose();
terminalOpen.value = false;
};
const handleClose = () => {
onClose();
terminalVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style lang="scss" scoped>
.terminal {
height: calc(100vh - 180px);
}
</style>
+8 -3
View File
@@ -120,7 +120,7 @@
<ConfigDrawer ref="configRef" @updated="search" />
<AppUpgrade ref="upgradeRef" @close="search" />
<ComposeLogs ref="composeLogRef" />
<TerminalDialog ref="dialogTerminalRef" />
<AgentTerminalDialog ref="dialogTerminalRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
</div>
</template>
@@ -143,7 +143,7 @@ import ConfigDrawer from '@/views/ai/agents/agent/config/index.vue';
import AppUpgrade from '@/views/app-store/installed/upgrade/index.vue';
import TaskLog from '@/components/log/task/index.vue';
import ComposeLogs from '@/components/log/compose/index.vue';
import TerminalDialog from '@/views/container/container/terminal/index.vue';
import AgentTerminalDialog from '@/views/ai/agents/agent/components/terminal.vue';
import i18n from '@/lang';
import PortJumpDialog from '@/components/port-jump/index.vue';
import DockerStatus from '@/views/container/docker-status/index.vue';
@@ -307,7 +307,12 @@ const openLog = (row: AI.AgentItem) => {
const openTerminal = (row: AI.AgentItem) => {
const title = i18n.global.t('aiTools.agents.agent') + ' ' + row.name;
dialogTerminalRef.value?.acceptParams({ containerID: row.containerName, title });
dialogTerminalRef.value?.acceptParams({
containerID: row.containerName,
title,
users: ['node', 'root'],
shell: '/bin/bash',
});
};
const openWorkDir = (row: AI.AgentItem) => {