🔨 优化代码逻辑.

This commit is contained in:
lijiahangmax
2025-06-28 16:38:15 +08:00
parent f1771ce676
commit eb81a030e3
25 changed files with 125 additions and 139 deletions

View File

@@ -13,7 +13,7 @@
ref="formRef"
label-align="right"
:auto-label-width="true"
:rules="formRules">
:rules="commandSnippetFormRules">
<!-- 名称 -->
<a-form-item field="name" label="名称">
<a-input v-model="formModel.name"
@@ -52,8 +52,8 @@
import { createCommandSnippet, updateCommandSnippet } from '@/api/terminal/command-snippet';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from './types/form.rules';
import { Message } from '@arco-design/web-vue';
import { commandSnippetFormRules } from '../../types/form.rules';
import CommandSnippetGroupSelector from '@/components/terminal/command-snippet/gruop/selector/index.vue';
const { visible, setVisible } = useVisible();

View File

@@ -1,24 +0,0 @@
import type { FieldRule } from '@arco-design/web-vue';
export const groupId = [{
message: '请选择分组'
}] as FieldRule[];
export const name = [{
required: true,
message: '请输入名称'
}, {
maxLength: 64,
message: '名称长度不能大于64位'
}] as FieldRule[];
export const command = [{
required: true,
message: '请输入代码片段'
}] as FieldRule[];
export default {
groupId,
name,
command,
} as Record<string, FieldRule | FieldRule[]>;

View File

@@ -13,7 +13,7 @@
ref="formRef"
label-align="right"
:auto-label-width="true"
:rules="formRules">
:rules="bookmarkFormRules">
<!-- 名称 -->
<a-form-item field="name" label="名称">
<a-input v-model="formModel.name"
@@ -54,12 +54,11 @@
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from './types/form.rules';
import { createPathBookmark, updatePathBookmark } from '@/api/terminal/path-bookmark';
import { PathBookmarkType } from './types/const';
import { bookmarkFormRules } from '../../types/form.rules';
import { pathBookmarkTypeKey, PathBookmarkType } from '../../types/const';
import { useDictStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import { pathBookmarkTypeKey } from '../../types/const';
import PathBookmarkGroupSelector from '@/components/terminal/bookmark-path/group/selector/index.vue';
const { visible, setVisible } = useVisible();

View File

@@ -117,8 +117,7 @@
import { useTerminalStore } from '@/store';
import { useDebounceFn } from '@vueuse/core';
import { getParentPath } from '@/utils/file';
import { PathBookmarkType } from './types/const';
import { TerminalSessionTypes } from '../../types/const';
import { TerminalSessionTypes, PathBookmarkType } from '../../types/const';
const props = defineProps<{
item: PathBookmarkQueryResponse;

View File

@@ -1,5 +0,0 @@
// 路径书签类型
export const PathBookmarkType = {
FILE: 'FILE',
DIR: 'DIR',
};

View File

@@ -1,33 +0,0 @@
import type { FieldRule } from '@arco-design/web-vue';
export const groupId = [{
message: '请选择分组'
}] as FieldRule[];
export const name = [{
required: true,
message: '请输入名称'
}, {
maxLength: 64,
message: '名称长度不能大于64位'
}] as FieldRule[];
export const type = [{
required: true,
message: '请选择类型'
}] as FieldRule[];
export const path = [{
required: true,
message: '请输入路径'
}, {
maxLength: 1000,
message: '名称长度不能大于1000位'
}] as FieldRule[];
export default {
groupId,
name,
type,
path,
} as Record<string, FieldRule | FieldRule[]>;

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="session.status.connectStatus === TerminalStatus.CONNECTED">
<div v-if="session.state.connectStatus === TerminalStatus.CONNECTED">
<!-- 工具栏 -->
<a-popover v-model:popup-visible="visible"
:title="undefined"

View File

@@ -1,11 +1,11 @@
<template>
<div v-if="session.status.connectStatus !== TerminalStatus.CONNECTED && visible">
<div v-if="session.state.connectStatus !== TerminalStatus.CONNECTED && visible">
<!-- 连接中 -->
<a-spin v-if="session.status.connectStatus === TerminalStatus.CONNECTING"
<a-spin v-if="session.state.connectStatus === TerminalStatus.CONNECTING"
tip="正在连接会话..."
dot />
<!-- 会话关闭 -->
<a-card v-if="session.status.connectStatus === TerminalStatus.CLOSED"
<a-card v-if="session.state.connectStatus === TerminalStatus.CLOSED"
class="rdp-status-wrapper"
title="会话已关闭">
<!-- 错误信息 -->
@@ -20,18 +20,18 @@
</a-descriptions-item>
<!-- 错误码 -->
<a-descriptions-item label="错误码">
{{ session.status.closeCode }}
{{ session.state.closeCode }}
</a-descriptions-item>
<!-- 错误信息 -->
<a-descriptions-item label="错误信息">
<span class="span-red">
<!-- 异地登录 -->
<template v-if="session.status.closeCode === TerminalCloseCode.LOGGED_ELSEWHERE">
<template v-if="session.state.closeCode === TerminalCloseCode.LOGGED_ELSEWHERE">
{{ TerminalMessages.loggedElsewhere }} ({{ dateFormat() }})
</template>
<!-- 其他错误 -->
<template v-else>
{{ session.status.closeMessage ?? '-' }}
{{ session.state.closeMessage ?? '-' }}
</template>
</span>
</a-descriptions-item>
@@ -39,7 +39,7 @@
<!-- 按钮 -->
<a-space class="status-button">
<a-button @click="setVisible(false)">关闭</a-button>
<a-button v-if="session.status.closeCode !== TerminalCloseCode.FORCE && session.status.canReconnect"
<a-button v-if="session.state.closeCode !== TerminalCloseCode.FORCE && session.state.canReconnect"
type="primary"
@click="reOpenSession(session.sessionKey)">
重连

View File

@@ -45,7 +45,7 @@
</div>
</div>
<!-- 已关闭-右侧操作 -->
<div v-if="session?.status.connected === false && closeMessage !== undefined"
<div v-if="session?.state.connected === false && closeMessage !== undefined"
class="sftp-table-header-right">
<!-- 错误信息 -->
<a-tag class="close-message"
@@ -54,7 +54,7 @@
已断开: {{ closeMessage }}
</a-tag>
<!-- 重连 -->
<a-tooltip v-if="session?.status.connected === false && session?.status.canReconnect"
<a-tooltip v-if="session?.state.connected === false && session?.state.canReconnect"
position="top"
:mini="true"
:overlay-inverse="true"
@@ -245,7 +245,7 @@
// 设置命令编辑模式
const setPathEditable = (editable: boolean) => {
// 检查是否断开
if (editable && !props.session?.status.connected) {
if (editable && !props.session?.state.connected) {
return;
}
pathEditable.value = editable;
@@ -267,7 +267,7 @@
// 加载文件列表
const loadFileList = (path: string = props.currentPath) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
emits('loadFile', path);

View File

@@ -3,7 +3,7 @@
row-key="path"
ref="tableRef"
class="sftp-table"
:columns="columns"
:columns="sftpColumns"
:row-selection="rowSelection"
:sticky-header="true"
:data="list"
@@ -149,7 +149,7 @@
import { dateFormat } from '@/utils';
import { setAutoFocus } from '@/utils/dom';
import { copy } from '@/hooks/copy';
import columns from './types/table.columns';
import { sftpColumns } from '@/views/terminal/types/table.columns';
import { FILE_TYPE, openSftpChmodModalKey, openSftpMoveModalKey } from '@/views/terminal/types/const';
const props = defineProps<{
@@ -205,7 +205,7 @@
const clickFilename = (record: TableData) => {
if (record.isDir) {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
// 进入文件夹
@@ -218,7 +218,7 @@
// 编辑文件
const editFile = (record: TableData) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
emits('editFile', record.name, record.path);
@@ -228,7 +228,7 @@
// 删除文件
const deleteFile = (path: string) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
emits('deleteFile', [path]);
@@ -237,7 +237,7 @@
// 下载文件
const downloadFile = (path: string) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
emits('download', [path], false);
@@ -246,7 +246,7 @@
// 移动文件
const moveFile = (path: string) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
openSftpMoveModal(props.session?.sessionKey as string, path);
@@ -255,7 +255,7 @@
// 文件提权
const chmodFile = (path: string, permission: number) => {
// 检查是否断开
if (!props.session?.status.connected) {
if (!props.session?.state.connected) {
return;
}
openSftpChmodModal(props.session?.sessionKey as string, path, permission);

View File

@@ -23,8 +23,8 @@
<!-- 连接状态 -->
<a-badge v-if="preference.sshActionBarSetting.connectStatus !== false"
class="status-bridge"
:status="getDictValue(connectStatusKey, session ? session.status.connectStatus : TerminalStatus.CONNECTING, 'status')"
:text="getDictValue(connectStatusKey, session ? session.status.connectStatus : TerminalStatus.CONNECTING)" />
:status="getDictValue(connectStatusKey, session ? session.state.connectStatus : TerminalStatus.CONNECTING, 'status')"
:text="getDictValue(connectStatusKey, session ? session.state.connectStatus : TerminalStatus.CONNECTING)" />
</div>
</div>
</template>

View File

@@ -42,7 +42,7 @@ export interface SessionHostInfo {
}
// 会话状态
export interface ReactiveSessionStatus {
export interface ReactiveSessionState {
// 连接状态
connectStatus: number;
// 是否已连接
@@ -54,7 +54,7 @@ export interface ReactiveSessionStatus {
}
// guacd 会话状态
export interface GuacdReactiveSessionStatus extends ReactiveSessionStatus {
export interface GuacdReactiveSessionStatus extends ReactiveSessionState {
// 关闭码
closeCode: number;
// 关闭信息
@@ -76,7 +76,7 @@ export interface IDomViewportHandler {
}
// 终端会话定义
export interface ITerminalSession<Status extends ReactiveSessionStatus = ReactiveSessionStatus> {
export interface ITerminalSession<State extends ReactiveSessionState = ReactiveSessionState> {
readonly type: string;
// 会话主机信息
@@ -88,7 +88,7 @@ export interface ITerminalSession<Status extends ReactiveSessionStatus = Reactiv
// 后端交互的唯一值 后端的 sessionId
sessionId: string;
// 会话状态
readonly status: Reactive<Status>;
readonly state: Reactive<State>;
// 重新初始化
reInit: () => Promise<void>;

View File

@@ -131,10 +131,10 @@ export default abstract class BaseGuacdChannel<T extends ITerminalSession<GuacdR
this.onerror(new Guacamole.Status(closeCode as any, msg));
}
// 设置关闭原因
this.session.status.closeCode = closeCode;
this.session.status.closeMessage = msg || TerminalMessages.sessionClosed;
this.session.state.closeCode = closeCode;
this.session.state.closeMessage = msg || TerminalMessages.sessionClosed;
// 设置重连状态
this.session.status.canReconnect = TerminalCloseCode.FORCE !== closeCode;
this.session.state.canReconnect = TerminalCloseCode.FORCE !== closeCode;
// 设置已关闭
this.setState(Guacamole.Tunnel.State.CLOSED);
this.session.setClosed();

View File

@@ -32,7 +32,7 @@ export default class SftpChannel extends BaseTerminalChannel<ISftpSession> imple
}
const codeNumber = Number.parseInt(code);
this.triggerClosed = true;
this.session.status.canReconnect = TerminalCloseCode.FORCE !== codeNumber;
this.session.state.canReconnect = TerminalCloseCode.FORCE !== codeNumber;
// 设置已关闭
this.session.setClosed();
// sftp 设置状态

View File

@@ -36,13 +36,13 @@ export default class SshChannel extends BaseTerminalChannel<ISshSession> impleme
if (this.triggerClosed) {
return;
}
const beforeConnected = this.session.status.connected;
const beforeConnected = this.session.state.connected;
this.triggerClosed = true;
// 设置重连状态
this.session.status.canReconnect = TerminalCloseCode.FORCE !== Number.parseInt(code);
this.session.state.canReconnect = TerminalCloseCode.FORCE !== Number.parseInt(code);
// 拼接关闭消息
this.session.write((beforeConnected ? '\r\n\r\n' : '') + ansi(91, msg || ''));
if (this.session.status.canReconnect) {
if (this.session.state.canReconnect) {
this.session.write('\r\n' + ansi(91, TerminalMessages.waitingReconnect) + '\r\n');
}
// 设置已关闭

View File

@@ -82,9 +82,9 @@ export default class SshSessionHandler implements ISshSessionHandler {
case 'openSftp':
case 'uploadFile':
case 'checkAppendMissing':
return this.session.status.canWrite;
return this.session.state.canWrite;
case 'disconnect':
return this.session.status.connected;
return this.session.state.connected;
default:
return true;
}
@@ -193,7 +193,7 @@ export default class SshSessionHandler implements ISshSessionHandler {
// 字号增加
private fontSizeAdd(addSize: number) {
this.inst.options['fontSize'] = this.inst.options['fontSize'] as number + addSize;
if (this.session.status.connected) {
if (this.session.state.connected) {
this.session.fit();
this.inst.focus();
}

View File

@@ -1,21 +1,21 @@
import type { Reactive } from 'vue';
import { reactive } from 'vue';
import type { ITerminalChannel, ITerminalSession, ReactiveSessionStatus, SessionHostInfo, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import type { ITerminalChannel, ITerminalSession, ReactiveSessionState, SessionHostInfo, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import { TerminalStatus } from '@/views/terminal/types/const';
// 会话基类
export default abstract class BaseSession<Status extends ReactiveSessionStatus, Channel extends ITerminalChannel>
implements ITerminalSession<Status> {
export default abstract class BaseSession<State extends ReactiveSessionState, Channel extends ITerminalChannel>
implements ITerminalSession<State> {
public readonly type: string;
public readonly info: SessionHostInfo;
public readonly panelIndex: number;
public readonly status: Reactive<Status>;
public readonly state: Reactive<State>;
public readonly sessionKey: string;
public sessionId: string;
protected channel: Channel;
protected constructor(item: TerminalSessionTabItem, reactiveStatus: Partial<Status>) {
protected constructor(item: TerminalSessionTabItem, state: Partial<State>) {
this.type = item.type;
this.info = {
hostId: item.hostId,
@@ -26,13 +26,13 @@ export default abstract class BaseSession<Status extends ReactiveSessionStatus,
this.panelIndex = item.panelIndex;
this.sessionKey = item.key;
this.sessionId = item.key;
this.status = reactive({
this.state = reactive({
connectStatus: TerminalStatus.CONNECTING,
connected: false,
canWrite: false,
canReconnect: false,
...reactiveStatus,
} as Status);
...state,
} as State);
this.channel = undefined as unknown as Channel;
}
@@ -62,28 +62,28 @@ export default abstract class BaseSession<Status extends ReactiveSessionStatus,
// 设置是否可写
setCanWrite(canWrite: boolean): void {
this.status.canWrite = canWrite;
this.state.canWrite = canWrite;
}
// 设置连接中
setConnecting(): void {
this.status.connected = false;
this.status.canWrite = false;
this.status.connectStatus = TerminalStatus.CONNECTING;
this.state.connected = false;
this.state.canWrite = false;
this.state.connectStatus = TerminalStatus.CONNECTING;
}
// 设置已连接
setConnected(): void {
// 设置状态
this.status.connected = true;
this.status.connectStatus = TerminalStatus.CONNECTED;
this.state.connected = true;
this.state.connectStatus = TerminalStatus.CONNECTED;
}
// 设置已关闭
setClosed(): void {
this.status.connected = false;
this.status.canWrite = false;
this.status.connectStatus = TerminalStatus.CLOSED;
this.state.connected = false;
this.state.canWrite = false;
this.state.connectStatus = TerminalStatus.CLOSED;
}
}

View File

@@ -128,7 +128,7 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
// 定时检查是否连接成功
this.connectTimeoutId = window.setTimeout(() => {
// 未连接上证明连接超时
if (!this.status.connected) {
if (!this.state.connected) {
this.channel.closeTunnel(TerminalCloseCode.CONNECT_TIMEOUT, TerminalMessages.rdpConnectTimeout);
}
}, CONNECT_TIMEOUT);
@@ -217,7 +217,7 @@ export default class RdpSession extends BaseSession<GuacdReactiveSessionStatus,
// 是否可写
isWriteable(): boolean {
return this.status.connected && this.status.canWrite;
return this.state.connected && this.state.canWrite;
}
// 设置为已关闭

View File

@@ -1,4 +1,4 @@
import type { ISftpChannel, ISftpSession, ISftpSessionHandler, ReactiveSessionStatus, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import type { ISftpChannel, ISftpSession, ISftpSessionHandler, ReactiveSessionState, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import { h } from 'vue';
import { InputProtocol } from '@/views/terminal/types/protocol';
import { Modal } from '@arco-design/web-vue';
@@ -6,7 +6,7 @@ import BaseSession from './base-session';
import SftpChannel from '../channel/sftp-channel';
// SFTP 会话实现
export default class SftpSession extends BaseSession<ReactiveSessionStatus, ISftpChannel> implements ISftpSession {
export default class SftpSession extends BaseSession<ReactiveSessionState, ISftpChannel> implements ISftpSession {
public handler: ISftpSessionHandler;

View File

@@ -1,4 +1,4 @@
import type { ISshChannel, ISshSession, ISshSessionHandler, ReactiveSessionStatus, SshInitConfig, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import type { ISshChannel, ISshSession, ISshSessionHandler, ReactiveSessionState, SshInitConfig, TerminalSessionTabItem } from '@/views/terminal/interfaces';
import type { UnwrapRef } from 'vue';
import type { ISearchOptions } from '@xterm/addon-search';
import { SearchAddon } from '@xterm/addon-search';
@@ -23,7 +23,7 @@ import SshChannel from '../channel/ssh-channel';
import SshSessionHandler from '../handler/ssh-session-handler';
// SSH 会话实现
export default class SshSession extends BaseSession<ReactiveSessionStatus, ISshChannel> implements ISshSession {
export default class SshSession extends BaseSession<ReactiveSessionState, ISshChannel> implements ISshSession {
public inst: Terminal;
@@ -102,9 +102,9 @@ export default class SshSession extends BaseSession<ReactiveSessionStatus, ISshC
e.preventDefault();
}
// 检查重新连接
if (!this.status.connected && this.status.canReconnect && e.key === 'Enter') {
if (!this.state.connected && this.state.canReconnect && e.key === 'Enter') {
// 防止重复回车
this.status.canReconnect = false;
this.state.canReconnect = false;
// 异步作用域重新连接
setTimeout(async () => {
await useTerminalStore().reOpenSession(this.sessionKey);
@@ -129,7 +129,7 @@ export default class SshSession extends BaseSession<ReactiveSessionStatus, ISshC
private registerEvent(dom: HTMLElement, preference: UnwrapRef<TerminalPreference>) {
// 注册输入事件
this.inst.onData(s => {
if (!this.status.canWrite || !this.status.connected) {
if (!this.state.canWrite || !this.state.connected) {
return;
}
// 输入
@@ -153,7 +153,7 @@ export default class SshSession extends BaseSession<ReactiveSessionStatus, ISshC
}
// 注册 resize 事件
this.inst.onResize(({ cols, rows }) => {
if (!this.status.connected) {
if (!this.state.connected) {
return;
}
this.channel.send(InputProtocol.RESIZE, {
@@ -165,7 +165,7 @@ export default class SshSession extends BaseSession<ReactiveSessionStatus, ISshC
addEventListen(dom, 'contextmenu', async () => {
// 右键粘贴逻辑
if (preference.interactSetting.rightClickPaste) {
if (!this.status.canWrite || !this.status.connected) {
if (!this.state.canWrite || !this.state.connected) {
return;
}
// 未开启右键选中 || 开启并无选中的内容则粘贴

View File

@@ -89,7 +89,7 @@ export default class RdpFileDownloadTask extends BaseFileTransferTask implements
}
this.state.aborted = true;
try {
if (this.session.status.connected) {
if (this.session.state.connected) {
if (this.stream) {
// 发送 ACK
this.stream.sendAck('Aborted', Guacamole.Status.Code.RESOURCE_CLOSED);

View File

@@ -117,7 +117,7 @@ export default class RdpFileUploadTask extends BaseFileTransferTask implements I
}
this.state.aborted = true;
try {
if (this.session.status.connected) {
if (this.session.state.connected) {
// 关闭流
if (this.stream) {
this.stream.sendEnd();

View File

@@ -485,6 +485,12 @@ export const TransferReceiver = {
ABORT: 'abort',
};
// 路径书签类型
export const PathBookmarkType = {
FILE: 'FILE',
DIR: 'DIR',
};
// 打开 extraModal key
export const openExtraModalKey = Symbol();

View File

@@ -0,0 +1,44 @@
import type { FieldRule } from '@arco-design/web-vue';
// 代码片段规则
export const commandSnippetFormRules = {
groupId: [{
message: '请选择分组'
}],
name: [{
required: true,
message: '请输入名称'
}, {
maxLength: 64,
message: '名称长度不能大于64位'
}],
command: [{
required: true,
message: '请输入代码片段'
}],
} as Record<string, FieldRule | FieldRule[]>;
// 书签路径规则
export const bookmarkFormRules = {
groupId: [{
message: '请选择分组'
}],
name: [{
required: true,
message: '请输入名称'
}, {
maxLength: 64,
message: '名称长度不能大于64位'
}],
type: [{
required: true,
message: '请选择类型'
}],
path: [{
required: true,
message: '请输入路径'
}, {
maxLength: 1000,
message: '路径长度不能大于1000位'
}],
} as Record<string, FieldRule | FieldRule[]>;

View File

@@ -1,8 +1,8 @@
import type { TableColumnData } from '@arco-design/web-vue';
import { getFileSize } from '@/utils/file';
// 表格列
const columns = [
// SFTP 表格列
export const sftpColumns = [
{
title: '名称',
dataIndex: 'name',
@@ -45,4 +45,4 @@ const columns = [
},
] as TableColumnData[];
export default columns;
export default sftpColumns;