mirror of https://github.com/1Panel-dev/1Panel
feat: 文件列表组件新增创建文件夹功能 (#5647)
parent
2a1ec4db75
commit
9da5e67183
|
@ -74,7 +74,7 @@ ErrFileToLarge: "file is too large"
|
||||||
ErrPathNotFound: "Path is not found"
|
ErrPathNotFound: "Path is not found"
|
||||||
ErrMovePathFailed: "The target path cannot contain the original path!"
|
ErrMovePathFailed: "The target path cannot contain the original path!"
|
||||||
ErrLinkPathNotFound: "Target path does not exist!"
|
ErrLinkPathNotFound: "Target path does not exist!"
|
||||||
ErrFileIsExit: "File already exists!"
|
ErrFileIsExit: "File or directory already exists!"
|
||||||
ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
|
ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
|
||||||
ErrFileDownloadDir: "Download folder not supported"
|
ErrFileDownloadDir: "Download folder not supported"
|
||||||
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
|
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
|
||||||
|
|
|
@ -75,7 +75,7 @@ ErrFileToLarge: "文件超過10M,無法打開"
|
||||||
ErrPathNotFound: "目錄不存在"
|
ErrPathNotFound: "目錄不存在"
|
||||||
ErrMovePathFailed: "目標路徑不能包含原路徑!"
|
ErrMovePathFailed: "目標路徑不能包含原路徑!"
|
||||||
ErrLinkPathNotFound: "目標路徑不存在!"
|
ErrLinkPathNotFound: "目標路徑不存在!"
|
||||||
ErrFileIsExit: "文件已存在!"
|
ErrFileIsExit: "文件或文件夾已存在!"
|
||||||
ErrFileUpload: "{{ .name }} 上傳文件失敗 {{ .detail}}"
|
ErrFileUpload: "{{ .name }} 上傳文件失敗 {{ .detail}}"
|
||||||
ErrFileDownloadDir: "不支持下載文件夾"
|
ErrFileDownloadDir: "不支持下載文件夾"
|
||||||
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
|
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
|
||||||
|
|
|
@ -74,7 +74,7 @@ ErrFileToLarge: "文件超过10M,无法打开"
|
||||||
ErrPathNotFound: "目录不存在"
|
ErrPathNotFound: "目录不存在"
|
||||||
ErrMovePathFailed: "目标路径不能包含原路径!"
|
ErrMovePathFailed: "目标路径不能包含原路径!"
|
||||||
ErrLinkPathNotFound: "目标路径不存在!"
|
ErrLinkPathNotFound: "目标路径不存在!"
|
||||||
ErrFileIsExit: "文件已存在!"
|
ErrFileIsExit: "文件或文件夹已存在!"
|
||||||
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
|
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
|
||||||
ErrFileDownloadDir: "不支持下载文件夹"
|
ErrFileDownloadDir: "不支持下载文件夹"
|
||||||
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
||||||
|
|
|
@ -67,7 +67,19 @@
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon>
|
<svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon>
|
||||||
<svg-icon v-else className="table-icon" iconName="p-file-normal"></svg-icon>
|
<svg-icon v-else className="table-icon" iconName="p-file-normal"></svg-icon>
|
||||||
<el-link :underline="false" @click="open(row)">{{ row.name }}</el-link>
|
<el-link v-if="!row.isCreate" :underline="false" @click="open(row)">{{ row.name }}</el-link>
|
||||||
|
<el-input
|
||||||
|
v-else
|
||||||
|
ref="rowRefs"
|
||||||
|
v-model="newFlower"
|
||||||
|
style="width: 240px"
|
||||||
|
placeholder="new flower"
|
||||||
|
@input="handleChange(newFlower, row)"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button :icon="Check" @click="createFlower(row)" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
@ -83,9 +95,16 @@
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<el-button link @click="onAddRow" type="primary">
|
||||||
|
{{ $t('commons.button.create') }}
|
||||||
|
</el-button>
|
||||||
<div class="button">
|
<div class="button">
|
||||||
<el-button @click="closePage">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button @click="closePage">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
<el-button type="primary" @click="selectFile">{{ $t('commons.button.confirm') }}</el-button>
|
<el-button type="primary" @click="selectFile" :disabled="disBtn">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
@ -93,19 +112,24 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { File } from '@/api/interface/file';
|
import { File } from '@/api/interface/file';
|
||||||
import { GetFilesList } from '@/api/modules/files';
|
import { CreateFile, GetFilesList } from '@/api/modules/files';
|
||||||
import { Folder } from '@element-plus/icons-vue';
|
import { Folder, Check, HomeFilled, Close } from '@element-plus/icons-vue';
|
||||||
import BreadCrumbs from '@/components/bread-crumbs/index.vue';
|
import BreadCrumbs from '@/components/bread-crumbs/index.vue';
|
||||||
import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue';
|
import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue';
|
||||||
import { onMounted, onUpdated, reactive, ref } from 'vue';
|
import { onMounted, onUpdated, reactive, ref, nextTick } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||||
|
|
||||||
const rowName = ref('');
|
const rowName = ref('');
|
||||||
const data = ref();
|
const data = ref([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const paths = ref<string[]>([]);
|
const paths = ref<string[]>([]);
|
||||||
const req = reactive({ path: '/', expand: true, page: 1, pageSize: 300, showHidden: true });
|
const req = reactive({ path: '/', expand: true, page: 1, pageSize: 300, showHidden: true });
|
||||||
const selectRow = ref();
|
const selectRow = ref();
|
||||||
|
const rowRefs = ref();
|
||||||
const popoverVisible = ref(false);
|
const popoverVisible = ref(false);
|
||||||
|
const newFlower = ref();
|
||||||
|
const disBtn = ref(false);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
path: {
|
path: {
|
||||||
|
@ -129,6 +153,7 @@ const props = defineProps({
|
||||||
const em = defineEmits(['choose']);
|
const em = defineEmits(['choose']);
|
||||||
|
|
||||||
const checkFile = (row: any) => {
|
const checkFile = (row: any) => {
|
||||||
|
disBtn.value = row.isCreate;
|
||||||
selectRow.value = row;
|
selectRow.value = row;
|
||||||
rowName.value = selectRow.value.name;
|
rowName.value = selectRow.value.name;
|
||||||
};
|
};
|
||||||
|
@ -170,21 +195,25 @@ const open = async (row: File.File) => {
|
||||||
} else {
|
} else {
|
||||||
req.path = req.path + '/' + name;
|
req.path = req.path + '/' + name;
|
||||||
}
|
}
|
||||||
search(req);
|
await search(req);
|
||||||
}
|
}
|
||||||
|
selectRow.value = {};
|
||||||
rowName.value = '';
|
rowName.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const jump = async (index: number) => {
|
const jump = async (index: number) => {
|
||||||
let path = '/';
|
let path = '';
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
if (index !== -1) {
|
||||||
const jPaths = paths.value.slice(0, index + 1);
|
const jPaths = paths.value.slice(0, index + 1);
|
||||||
for (let i in jPaths) {
|
path = '/' + jPaths.join('/');
|
||||||
path = path + '/' + jPaths[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
path = path || '/';
|
||||||
req.path = path;
|
req.path = path;
|
||||||
search(req);
|
selectRow.value = {};
|
||||||
|
rowName.value = '';
|
||||||
|
await search(req);
|
||||||
popoverVisible.value = true;
|
popoverVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,7 +222,7 @@ const search = async (req: File.ReqFile) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await GetFilesList(req)
|
await GetFilesList(req)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items || [];
|
||||||
req.path = res.data.path;
|
req.path = res.data.path;
|
||||||
const pathArray = req.path.split('/');
|
const pathArray = req.path.split('/');
|
||||||
paths.value = [];
|
paths.value = [];
|
||||||
|
@ -208,6 +237,62 @@ const search = async (req: File.ReqFile) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let addForm = reactive({ path: '', name: '', isDir: true, mode: 0o755, isLink: false, isSymlink: true, linkPath: '' });
|
||||||
|
|
||||||
|
const onAddRow = async () => {
|
||||||
|
const createRow = data.value?.find((row: { isCreate: any }) => row.isCreate);
|
||||||
|
if (createRow) {
|
||||||
|
MsgWarning(i18n.global.t('commons.msg.creatingInfo'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newFlower.value = i18n.global.t('file.noNameFolder');
|
||||||
|
rowName.value = newFlower.value;
|
||||||
|
selectRow.value.name = newFlower.value;
|
||||||
|
const basePath = req.path === '/' ? req.path : `${req.path}/`;
|
||||||
|
selectRow.value.path = `${basePath}${newFlower.value}`;
|
||||||
|
data.value?.unshift({
|
||||||
|
path: selectRow.value.path,
|
||||||
|
isCreate: true,
|
||||||
|
isDir: true,
|
||||||
|
name: newFlower.value,
|
||||||
|
});
|
||||||
|
disBtn.value = true;
|
||||||
|
await nextTick();
|
||||||
|
rowRefs.value.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (value: string, row: any) => {
|
||||||
|
if (rowName.value === row.name) {
|
||||||
|
selectRow.value.name = value;
|
||||||
|
rowName.value = value;
|
||||||
|
row.name = value;
|
||||||
|
const basePath = req.path === '/' ? req.path : `${req.path}/`;
|
||||||
|
selectRow.value.path = `${basePath}${value}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createFlower = async (row: any) => {
|
||||||
|
const basePath = req.path === '/' ? req.path : `${req.path}/`;
|
||||||
|
addForm.path = `${basePath}${newFlower.value}`;
|
||||||
|
if (addForm.path.indexOf('.1panel_clash') > -1) {
|
||||||
|
MsgWarning(i18n.global.t('file.clashDitNotSupport'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addForm.name = newFlower.value;
|
||||||
|
let addItem = {};
|
||||||
|
Object.assign(addItem, addForm);
|
||||||
|
loading.value = true;
|
||||||
|
CreateFile(addItem as File.FileCreate)
|
||||||
|
.then(() => {
|
||||||
|
row.isCreate = false;
|
||||||
|
disBtn.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.path != '') {
|
if (props.path != '') {
|
||||||
req.path = props.path;
|
req.path = props.path;
|
||||||
|
|
|
@ -146,6 +146,7 @@ const message = {
|
||||||
refreshSuccess: 'Refresh successful',
|
refreshSuccess: 'Refresh successful',
|
||||||
rootInfoErr: "It's already the root directory",
|
rootInfoErr: "It's already the root directory",
|
||||||
resetSuccess: 'Reset successful',
|
resetSuccess: 'Reset successful',
|
||||||
|
creatingInfo: 'Creating, no need for this operation',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: 'UserName',
|
username: 'UserName',
|
||||||
|
@ -1249,6 +1250,7 @@ const message = {
|
||||||
saveContentAndClose: 'The file has been modified, do you want to save and close it?',
|
saveContentAndClose: 'The file has been modified, do you want to save and close it?',
|
||||||
saveAndOpenNewFile: 'The file has been modified, do you want to save and open the new file?',
|
saveAndOpenNewFile: 'The file has been modified, do you want to save and open the new file?',
|
||||||
noEdit: 'The file has not been modified, no need to do this!',
|
noEdit: 'The file has not been modified, no need to do this!',
|
||||||
|
noNameFolder: 'Untitled Folder',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: 'Auto Start',
|
autoStart: 'Auto Start',
|
||||||
|
|
|
@ -146,6 +146,7 @@ const message = {
|
||||||
refreshSuccess: '重繪成功',
|
refreshSuccess: '重繪成功',
|
||||||
rootInfoErr: '已經是根目錄了',
|
rootInfoErr: '已經是根目錄了',
|
||||||
resetSuccess: '重置成功',
|
resetSuccess: '重置成功',
|
||||||
|
creatingInfo: '正在創建,無需此操作',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: '用戶名',
|
username: '用戶名',
|
||||||
|
@ -1186,6 +1187,7 @@ const message = {
|
||||||
saveContentAndClose: '檔案已被修改,是否保存並關閉?',
|
saveContentAndClose: '檔案已被修改,是否保存並關閉?',
|
||||||
saveAndOpenNewFile: '檔案已被修改,是否保存並打開新檔案?',
|
saveAndOpenNewFile: '檔案已被修改,是否保存並打開新檔案?',
|
||||||
noEdit: '檔案未修改,無需此操作!',
|
noEdit: '檔案未修改,無需此操作!',
|
||||||
|
noNameFolder: '未命名資料夾',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: '開機自啟',
|
autoStart: '開機自啟',
|
||||||
|
|
|
@ -146,6 +146,7 @@ const message = {
|
||||||
refreshSuccess: '刷新成功',
|
refreshSuccess: '刷新成功',
|
||||||
rootInfoErr: '已经是根目录了',
|
rootInfoErr: '已经是根目录了',
|
||||||
resetSuccess: '重置成功',
|
resetSuccess: '重置成功',
|
||||||
|
creatingInfo: '正在创建,无需此操作',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
|
@ -1187,6 +1188,7 @@ const message = {
|
||||||
saveContentAndClose: '文件已被修改,是否保存并关闭?',
|
saveContentAndClose: '文件已被修改,是否保存并关闭?',
|
||||||
saveAndOpenNewFile: '文件已被修改,是否保存并打开新文件?',
|
saveAndOpenNewFile: '文件已被修改,是否保存并打开新文件?',
|
||||||
noEdit: '文件未修改,无需此操作!',
|
noEdit: '文件未修改,无需此操作!',
|
||||||
|
noNameFolder: '未命名文件夹',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: '开机自启',
|
autoStart: '开机自启',
|
||||||
|
|
Loading…
Reference in New Issue