✨ 在线编辑文件.
This commit is contained in:
@@ -93,6 +93,18 @@
|
||||
emits('editor-mounted', editor);
|
||||
};
|
||||
|
||||
// 获取值
|
||||
const getValue = () => {
|
||||
return editor?.getValue();
|
||||
};
|
||||
|
||||
// 设置值
|
||||
const setValue = (value: string) => {
|
||||
editor?.setValue(value);
|
||||
};
|
||||
|
||||
defineExpose({ getValue, setValue });
|
||||
|
||||
// 监听主题变更
|
||||
watch(() => appStore.theme, (v) => {
|
||||
if (editor && props.theme === true) {
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import SftpSession from '../../handler/sftp-session';
|
||||
import { permission10toString } from '@/utils/file';
|
||||
import SftpSession from '../../handler/sftp-session';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { sessionManager } = useTerminalStore();
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<!-- 表头 -->
|
||||
<div class="sftp-editor-header">
|
||||
<!-- 左侧操作 -->
|
||||
<div class="sftp-editor-header-left">
|
||||
<div class="sftp-path-container">
|
||||
<!-- 当前路径 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
:content="path">
|
||||
<span>{{ name }}</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧操作 -->
|
||||
<a-space class="sftp-editor-header-right">
|
||||
<!-- 保存 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="保存">
|
||||
<span class="click-icon-wrapper header-action-icon"
|
||||
@click="emits('save')">
|
||||
<icon-save />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<!-- 关闭 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="关闭">
|
||||
<span class="click-icon-wrapper header-action-icon"
|
||||
@click="emits('close')">
|
||||
<icon-close />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'sftpEditorHeader'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ISftpSession } from '../../types/terminal.type';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
path: string;
|
||||
session: ISftpSession | undefined,
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['save', 'close']);
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@action-num: 2;
|
||||
@action-gap: 8px;
|
||||
@action-size: 26px;
|
||||
@actions-width: @action-num * (@action-size + @action-gap);
|
||||
|
||||
.sftp-editor-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&-left, &-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-left {
|
||||
width: calc(100% - @actions-width);
|
||||
}
|
||||
|
||||
&-right {
|
||||
width: @actions-width;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.sftp-path-container {
|
||||
width: 100%;
|
||||
height: @action-size;
|
||||
background: var(--color-fill-2);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.header-action-icon {
|
||||
font-size: 16px;
|
||||
padding: 4px;
|
||||
width: @action-size;
|
||||
height: @action-size;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<editor ref="editorRef"
|
||||
language="txt"
|
||||
:auto-focus="true"
|
||||
:theme="preference.theme.dark ? 'vs-dark' : 'vs'" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'sftpFileEditor'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
|
||||
const { preference } = useTerminalStore();
|
||||
|
||||
const editorRef = ref();
|
||||
|
||||
// 获取值
|
||||
const getValue = () => {
|
||||
return editorRef.value?.getValue();
|
||||
};
|
||||
|
||||
// 设置值
|
||||
const setValue = (value: string) => {
|
||||
editorRef.value?.setValue(value);
|
||||
};
|
||||
|
||||
defineExpose({ getValue, setValue });
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -68,7 +68,7 @@
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<!-- 编辑内容 -->
|
||||
<a-tooltip v-if="canEditable(record.attr)"
|
||||
<a-tooltip v-if="canEditable(record.sizeByte, record.attr)"
|
||||
position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
@@ -159,7 +159,7 @@
|
||||
selectedFiles: Array<string>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile']);
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile']);
|
||||
|
||||
const openSftpMoveModal = inject(openSftpMoveModalKey) as (sessionId: string, path: string) => void;
|
||||
const openSftpChmodModal = inject(openSftpChmodModalKey) as (sessionId: string, path: string, permission: number) => void;
|
||||
@@ -202,11 +202,12 @@
|
||||
};
|
||||
|
||||
// 是否可编辑
|
||||
const canEditable = (attr: string) => {
|
||||
const canEditable = (sizeByte: number, attr: string) => {
|
||||
const typeValue = formatFileType(attr).value;
|
||||
// 非文件夹和链接文件可以编辑
|
||||
// 非文件夹和链接文件 并且文件大小小于 2MB 可以编辑
|
||||
return FILE_TYPE.DIRECTORY.value !== typeValue
|
||||
&& FILE_TYPE.LINK_FILE.value !== typeValue;
|
||||
&& FILE_TYPE.LINK_FILE.value !== typeValue
|
||||
&& sizeByte <= 2 * 1024 * 1024;
|
||||
};
|
||||
|
||||
// 点击文件名称
|
||||
@@ -221,7 +222,8 @@
|
||||
|
||||
// 编辑文件
|
||||
const editFile = (record: TableData) => {
|
||||
// TODO
|
||||
emits('editFile', record.name, record.path);
|
||||
props.session?.getContent(record.path);
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a-split class="split-view"
|
||||
v-model:size="splitSize"
|
||||
:min="0.3"
|
||||
:disabled="!editView">
|
||||
:disabled="!editorView">
|
||||
<!-- 左侧面板表格 -->
|
||||
<template #first>
|
||||
<a-spin class="sftp-table-container"
|
||||
@@ -21,11 +21,24 @@
|
||||
:session="session"
|
||||
:list="fileList"
|
||||
:loading="tableLoading"
|
||||
@load-file="loadFiles" />
|
||||
@load-file="loadFiles"
|
||||
@edit-file="editFile" />
|
||||
</a-spin>
|
||||
</template>
|
||||
<template #second v-if="editView">
|
||||
<div>editor</div>
|
||||
<template #second v-if="editorView">
|
||||
<a-spin class="sftp-editor-container"
|
||||
:loading="editorLoading">
|
||||
<!-- 表头 -->
|
||||
<sftp-editor-header class="sftp-editor-header"
|
||||
:name="editorFileName"
|
||||
:path="editorFilePath"
|
||||
:session="session"
|
||||
@save="editorSave"
|
||||
@close="closeEditor" />
|
||||
<!-- 编辑器 -->
|
||||
<sftp-editor class="sftp-editor-wrapper"
|
||||
ref="editorRef" />
|
||||
</a-spin>
|
||||
</template>
|
||||
</a-split>
|
||||
<!-- 创建文件模态框 -->
|
||||
@@ -50,8 +63,10 @@
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { openSftpCreateModalKey, openSftpMoveModalKey, openSftpChmodModalKey } from '../../types/terminal.const';
|
||||
import SftpTable from './sftp-table.vue';
|
||||
import SftpTableHeader from './sftp-table-header.vue';
|
||||
import SftpTable from './sftp-table.vue';
|
||||
import SftpEditorHeader from './sftp-editor-header.vue';
|
||||
import SftpEditor from './sftp-editor.vue';
|
||||
import SftpCreateModal from './sftp-create-modal.vue';
|
||||
import SftpMoveModal from './sftp-move-modal.vue';
|
||||
import SftpChmodModal from './sftp-chmod-modal.vue';
|
||||
@@ -62,13 +77,17 @@
|
||||
|
||||
const { preference, sessionManager } = useTerminalStore();
|
||||
const { loading: tableLoading, setLoading: setTableLoading } = useLoading(true);
|
||||
const { loading: editorLoading, setLoading: setEditorLoading } = useLoading();
|
||||
|
||||
const session = ref<ISftpSession>();
|
||||
const currentPath = ref<string>('');
|
||||
const fileList = ref<Array<SftpFile>>([]);
|
||||
const selectFiles = ref<Array<string>>([]);
|
||||
const splitSize = ref(1);
|
||||
const editView = ref(true);
|
||||
const editorView = ref(false);
|
||||
const editorRef = ref();
|
||||
const editorFileName = ref('');
|
||||
const editorFilePath = ref('');
|
||||
const createModal = ref();
|
||||
const moveModal = ref();
|
||||
const chmodModal = ref();
|
||||
@@ -88,6 +107,30 @@
|
||||
chmodModal.value?.open(sessionId, path, permission);
|
||||
});
|
||||
|
||||
// 编辑文件
|
||||
const editFile = (name: string, path: string) => {
|
||||
setEditorLoading(true);
|
||||
splitSize.value = 0.6;
|
||||
editorView.value = true;
|
||||
editorFileName.value = name;
|
||||
editorFilePath.value = path;
|
||||
};
|
||||
|
||||
// 编辑器保存
|
||||
const editorSave = () => {
|
||||
setEditorLoading(true);
|
||||
const value = editorRef.value?.getValue() || '';
|
||||
session.value?.setContent(editorFilePath.value, value);
|
||||
};
|
||||
|
||||
// 关闭编辑器
|
||||
const closeEditor = () => {
|
||||
splitSize.value = 1;
|
||||
editorView.value = false;
|
||||
editorFileName.value = '';
|
||||
editorFilePath.value = '';
|
||||
};
|
||||
|
||||
// 连接成功回调
|
||||
const connectCallback = () => {
|
||||
loadFiles(undefined);
|
||||
@@ -133,10 +176,22 @@
|
||||
|
||||
// 接收获取文件内容响应
|
||||
const resolveSftpGetContent = (path: string, result: string, content: string) => {
|
||||
setEditorLoading(false);
|
||||
// 检查结果
|
||||
if (!checkResult(result, '加载失败')) {
|
||||
return;
|
||||
}
|
||||
editorRef.value?.setValue(content);
|
||||
};
|
||||
|
||||
// 接收修改文件内容响应
|
||||
const resolveSftpSetContent = (result: string, msg: string) => {
|
||||
setEditorLoading(false);
|
||||
// 检查结果
|
||||
if (!checkResult(result, msg)) {
|
||||
return;
|
||||
}
|
||||
Message.success('保存成功');
|
||||
};
|
||||
|
||||
// 初始化会话
|
||||
@@ -176,18 +231,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sftp-table-container {
|
||||
.sftp-table-container, .sftp-editor-container {
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.sftp-table-header {
|
||||
.sftp-table-header, .sftp-editor-header {
|
||||
width: 100%;
|
||||
height: @sftp-table-header-height;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.sftp-table-wrapper {
|
||||
.sftp-table-wrapper, .sftp-editor-wrapper {
|
||||
height: calc(100% - @sftp-table-header-height);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user