✨ 移动 & 提权.
This commit is contained in:
@@ -104,6 +104,9 @@ export function downloadFile(res: any, fileName: string) {
|
|||||||
* 10进制权限 转 字符串权限
|
* 10进制权限 转 字符串权限
|
||||||
*/
|
*/
|
||||||
export function permission10toString(permission: number) {
|
export function permission10toString(permission: number) {
|
||||||
|
if (permission === undefined) {
|
||||||
|
return '---';
|
||||||
|
}
|
||||||
const ps = (permission + '');
|
const ps = (permission + '');
|
||||||
let res = '';
|
let res = '';
|
||||||
for (let i = 0; i < ps.length; i++) {
|
for (let i = 0; i < ps.length; i++) {
|
||||||
@@ -124,6 +127,11 @@ export function permission10toString(permission: number) {
|
|||||||
res += 'x';
|
res += 'x';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (res.length <= 9) {
|
||||||
|
res = res.padEnd(9, '-');
|
||||||
|
} else {
|
||||||
|
res = res.substring(0, 9);
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div></div>
|
<a-modal v-model:visible="visible"
|
||||||
|
body-class="modal-form"
|
||||||
|
title-align="start"
|
||||||
|
title="修改权限"
|
||||||
|
:align-center="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
:on-before-ok="handlerOk">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
:style="{ width: '460px' }"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 18 }">
|
||||||
|
<!-- 文件路径 -->
|
||||||
|
<a-form-item field="path"
|
||||||
|
disabled
|
||||||
|
label="文件路径">
|
||||||
|
<a-input v-model="formModel.path"
|
||||||
|
placeholder="原始路径" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 文件权限 -->
|
||||||
|
<a-form-item field="mod"
|
||||||
|
label="文件权限"
|
||||||
|
:rules="[{ required: true, message: '请输入文件权限' }]">
|
||||||
|
<div class="mod-wrapper">
|
||||||
|
<!-- 权限输入框 -->
|
||||||
|
<a-input-number ref="modRef"
|
||||||
|
class="mod-input"
|
||||||
|
v-model="formModel.mod"
|
||||||
|
placeholder="请输入文件权限"
|
||||||
|
hide-button
|
||||||
|
@input="updatePreview" />
|
||||||
|
<!-- 权限预览 -->
|
||||||
|
<span class="mod-preview">{{ formModel.permission }}</span>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -9,9 +47,79 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
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';
|
||||||
|
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
|
const sessionId = ref();
|
||||||
|
const modRef = ref();
|
||||||
|
const formRef = ref();
|
||||||
|
const formModel = ref({
|
||||||
|
path: '',
|
||||||
|
mod: 0,
|
||||||
|
permission: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 修改预览
|
||||||
|
const updatePreview = (v: number) => {
|
||||||
|
formModel.value.permission = permission10toString(v);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开新增
|
||||||
|
const open = (session: string, path: string, permission: number) => {
|
||||||
|
sessionId.value = session;
|
||||||
|
formModel.value.path = path;
|
||||||
|
formModel.value.mod = permission;
|
||||||
|
formModel.value.permission = permission10toString(permission);
|
||||||
|
setVisible(true);
|
||||||
|
// 自动聚焦
|
||||||
|
nextTick(() => {
|
||||||
|
modRef.value?.focus();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handlerOk = async () => {
|
||||||
|
try {
|
||||||
|
// 验证参数
|
||||||
|
const error = await formRef.value.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 获取会话
|
||||||
|
const session = sessionManager.getSession(sessionId.value);
|
||||||
|
if (session instanceof SftpSession) {
|
||||||
|
session.chmod(formModel.value.path, formModel.value.mod);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.mod-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.mod-input {
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-preview {
|
||||||
|
width: 30%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,12 +6,10 @@
|
|||||||
:align-center="false"
|
:align-center="false"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
:unmount-on-close="true"
|
:unmount-on-close="true"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk">
|
||||||
@close="handleClose">
|
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
layout="horizontal"
|
|
||||||
:style="{ width: '460px' }"
|
:style="{ width: '460px' }"
|
||||||
:label-col-props="{ span: 6 }"
|
:label-col-props="{ span: 6 }"
|
||||||
:wrapper-col-props="{ span: 18 }">
|
:wrapper-col-props="{ span: 18 }">
|
||||||
@@ -35,13 +33,11 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useVisible from '@/hooks/visible';
|
import useVisible from '@/hooks/visible';
|
||||||
import useLoading from '@/hooks/loading';
|
|
||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import SftpSession from '../../handler/sftp-session';
|
import SftpSession from '../../handler/sftp-session';
|
||||||
|
|
||||||
const { visible, setVisible } = useVisible();
|
const { visible, setVisible } = useVisible();
|
||||||
const { loading, setLoading } = useLoading();
|
|
||||||
const { sessionManager } = useTerminalStore();
|
const { sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
const sessionId = ref();
|
const sessionId = ref();
|
||||||
@@ -68,7 +64,6 @@
|
|||||||
|
|
||||||
// 确定
|
// 确定
|
||||||
const handlerOk = async () => {
|
const handlerOk = async () => {
|
||||||
setLoading(true);
|
|
||||||
try {
|
try {
|
||||||
// 验证参数
|
// 验证参数
|
||||||
const error = await formRef.value.validate();
|
const error = await formRef.value.validate();
|
||||||
@@ -86,25 +81,11 @@
|
|||||||
session.mkdir(formModel.value.path);
|
session.mkdir(formModel.value.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 清空
|
|
||||||
handlerClear();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭
|
|
||||||
const handleClose = () => {
|
|
||||||
handlerClear();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 清空
|
|
||||||
const handlerClear = () => {
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -1,5 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div></div>
|
<a-modal v-model:visible="visible"
|
||||||
|
body-class="modal-form"
|
||||||
|
title-align="start"
|
||||||
|
title="移动文件"
|
||||||
|
:align-center="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
:on-before-ok="handlerOk">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
:style="{ width: '460px' }"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 18 }">
|
||||||
|
<!-- 原始路径 -->
|
||||||
|
<a-form-item field="path"
|
||||||
|
disabled
|
||||||
|
label="原始路径">
|
||||||
|
<a-input v-model="formModel.path"
|
||||||
|
placeholder="原始路径" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 目标路径 -->
|
||||||
|
<a-form-item field="target"
|
||||||
|
label="目标路径"
|
||||||
|
extra="目标路径可以是绝对路径/相对路径/名称 (可以包含 ./ ../)"
|
||||||
|
:rules="[{ required: true, message: '请输入目标路径' }]">
|
||||||
|
<a-input ref="targetRef"
|
||||||
|
v-model="formModel.target"
|
||||||
|
placeholder="请输入目标路径" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -9,6 +40,53 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import useVisible from '@/hooks/visible';
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
import SftpSession from '../../handler/sftp-session';
|
||||||
|
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
|
const sessionId = ref();
|
||||||
|
const targetRef = ref();
|
||||||
|
const formRef = ref();
|
||||||
|
const formModel = ref({
|
||||||
|
path: '',
|
||||||
|
target: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开新增
|
||||||
|
const open = (session: string, path: string) => {
|
||||||
|
sessionId.value = session;
|
||||||
|
formModel.value.path = path;
|
||||||
|
formModel.value.target = path;
|
||||||
|
setVisible(true);
|
||||||
|
// 自动聚焦
|
||||||
|
nextTick(() => {
|
||||||
|
targetRef.value?.focus();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handlerOk = async () => {
|
||||||
|
try {
|
||||||
|
// 验证参数
|
||||||
|
const error = await formRef.value.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 获取会话
|
||||||
|
const session = sessionManager.getSession(sessionId.value);
|
||||||
|
if (session instanceof SftpSession) {
|
||||||
|
session.move(formModel.value.path, formModel.value.target);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
arrow-class="terminal-tooltip-content"
|
arrow-class="terminal-tooltip-content"
|
||||||
content="提权">
|
content="提权">
|
||||||
<span class="click-icon-wrapper row-action-icon"
|
<span class="click-icon-wrapper row-action-icon"
|
||||||
@click="chmodFile(record.path, record.attr)">
|
@click="chmodFile(record.path, record.permission)">
|
||||||
<icon-user-group />
|
<icon-user-group />
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -144,13 +144,13 @@
|
|||||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||||
import type { SftpFile, ISftpSession } from '../../types/terminal.type';
|
import type { SftpFile, ISftpSession } from '../../types/terminal.type';
|
||||||
import type { VNodeRef } from 'vue';
|
import type { VNodeRef } from 'vue';
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch, inject } from 'vue';
|
||||||
import { useRowSelection } from '@/types/table';
|
import { useRowSelection } from '@/types/table';
|
||||||
import { dateFormat } from '@/utils';
|
import { dateFormat } from '@/utils';
|
||||||
|
import { setAutoFocus } from '@/utils/dom';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
import columns from './types/table.columns';
|
import columns from './types/table.columns';
|
||||||
import { FILE_TYPE } from '../../types/terminal.const';
|
import { FILE_TYPE, openSftpChmodModalKey, openSftpMoveModalKey } from '../../types/terminal.const';
|
||||||
import { setAutoFocus } from '@/utils/dom';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
session: ISftpSession | undefined;
|
session: ISftpSession | undefined;
|
||||||
@@ -161,6 +161,9 @@
|
|||||||
|
|
||||||
const emits = defineEmits(['update:selectedFiles', 'loadFile']);
|
const emits = defineEmits(['update:selectedFiles', 'loadFile']);
|
||||||
|
|
||||||
|
const openSftpMoveModal = inject(openSftpMoveModalKey) as (sessionId: string, path: string) => void;
|
||||||
|
const openSftpChmodModal = inject(openSftpChmodModalKey) as (sessionId: string, path: string, permission: number) => void;
|
||||||
|
|
||||||
const rowSelection = useRowSelection({ width: 40 });
|
const rowSelection = useRowSelection({ width: 40 });
|
||||||
const { copy } = useCopy();
|
const { copy } = useCopy();
|
||||||
|
|
||||||
@@ -211,6 +214,8 @@
|
|||||||
if (FILE_TYPE.DIRECTORY.value === formatFileType(record.attr).value) {
|
if (FILE_TYPE.DIRECTORY.value === formatFileType(record.attr).value) {
|
||||||
// 进入文件夹
|
// 进入文件夹
|
||||||
emits('loadFile', record.path);
|
emits('loadFile', record.path);
|
||||||
|
} else {
|
||||||
|
copy(record.name, '名称已复制');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -232,15 +237,12 @@
|
|||||||
|
|
||||||
// 移动文件
|
// 移动文件
|
||||||
const moveFile = (path: string) => {
|
const moveFile = (path: string) => {
|
||||||
// TODO openModal('path')
|
openSftpMoveModal(props.session?.sessionId as string, path);
|
||||||
console.log(path);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 文件提权
|
// 文件提权
|
||||||
const chmodFile = (path: string, attr: string) => {
|
const chmodFile = (path: string, permission: number) => {
|
||||||
// TODO openModal('path','mod')
|
openSftpChmodModal(props.session?.sessionId as string, path, permission);
|
||||||
console.log(path, attr);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化文件类型
|
// 格式化文件类型
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
</a-split>
|
</a-split>
|
||||||
<!-- 创建文件模态框 -->
|
<!-- 创建文件模态框 -->
|
||||||
<sftp-create-modal ref="createModal" />
|
<sftp-create-modal ref="createModal" />
|
||||||
|
<!-- 移动文件模态框 -->
|
||||||
|
<sftp-move-modal ref="moveModal" />
|
||||||
|
<!-- 文件提权模态框 -->
|
||||||
|
<sftp-chmod-modal ref="chmodModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,10 +49,12 @@
|
|||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
import { openSftpCreateModalKey } from '../../types/terminal.const';
|
import { openSftpCreateModalKey, openSftpMoveModalKey, openSftpChmodModalKey } from '../../types/terminal.const';
|
||||||
import SftpTable from './sftp-table.vue';
|
import SftpTable from './sftp-table.vue';
|
||||||
import SftpTableHeader from './sftp-table-header.vue';
|
import SftpTableHeader from './sftp-table-header.vue';
|
||||||
import SftpCreateModal from './sftp-create-modal.vue';
|
import SftpCreateModal from './sftp-create-modal.vue';
|
||||||
|
import SftpMoveModal from './sftp-move-modal.vue';
|
||||||
|
import SftpChmodModal from './sftp-chmod-modal.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
tab: TerminalTabItem
|
tab: TerminalTabItem
|
||||||
@@ -58,18 +64,30 @@
|
|||||||
const { loading: tableLoading, setLoading: setTableLoading } = useLoading(true);
|
const { loading: tableLoading, setLoading: setTableLoading } = useLoading(true);
|
||||||
|
|
||||||
const session = ref<ISftpSession>();
|
const session = ref<ISftpSession>();
|
||||||
const createModal = ref();
|
|
||||||
const currentPath = ref<string>('');
|
const currentPath = ref<string>('');
|
||||||
const fileList = ref<Array<SftpFile>>([]);
|
const fileList = ref<Array<SftpFile>>([]);
|
||||||
const selectFiles = ref<Array<string>>([]);
|
const selectFiles = ref<Array<string>>([]);
|
||||||
const splitSize = ref(1);
|
const splitSize = ref(1);
|
||||||
const editView = ref(true);
|
const editView = ref(true);
|
||||||
|
const createModal = ref();
|
||||||
|
const moveModal = ref();
|
||||||
|
const chmodModal = ref();
|
||||||
|
|
||||||
// 暴露打开创建方法
|
// 暴露打开创建模态框
|
||||||
provide(openSftpCreateModalKey, (sessionId: string, path: string, isTouch: boolean) => {
|
provide(openSftpCreateModalKey, (sessionId: string, path: string, isTouch: boolean) => {
|
||||||
createModal.value?.open(sessionId, path, isTouch);
|
createModal.value?.open(sessionId, path, isTouch);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 暴露打开移动模态框
|
||||||
|
provide(openSftpMoveModalKey, (sessionId: string, path: string) => {
|
||||||
|
moveModal.value?.open(sessionId, path);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 暴露打开提权模态框
|
||||||
|
provide(openSftpChmodModalKey, (sessionId: string, path: string, permission: number) => {
|
||||||
|
chmodModal.value?.open(sessionId, path, permission);
|
||||||
|
});
|
||||||
|
|
||||||
// 连接成功回调
|
// 连接成功回调
|
||||||
const connectCallback = () => {
|
const connectCallback = () => {
|
||||||
loadFiles(undefined);
|
loadFiles(undefined);
|
||||||
|
|||||||
@@ -287,6 +287,12 @@ export const openSshSettingModalKey = Symbol();
|
|||||||
// 打开 sftpCreateModal key
|
// 打开 sftpCreateModal key
|
||||||
export const openSftpCreateModalKey = Symbol();
|
export const openSftpCreateModalKey = Symbol();
|
||||||
|
|
||||||
|
// 打开 sftpMoveModal key
|
||||||
|
export const openSftpMoveModalKey = Symbol();
|
||||||
|
|
||||||
|
// 打开 sftpChmodModal key
|
||||||
|
export const openSftpChmodModalKey = Symbol();
|
||||||
|
|
||||||
// 字体后缀 兜底
|
// 字体后缀 兜底
|
||||||
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user