mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-27 20:49:03 +08:00
feat: 文件支持URL直接访问 (#543)
This commit is contained in:
parent
15d7d74c1b
commit
5c524f0d23
26
frontend/src/views/host/file-management/hooks/searchable.ts
Normal file
26
frontend/src/views/host/file-management/hooks/searchable.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
export function useSearchable(paths) {
|
||||
const searchableStatus = ref(false);
|
||||
const searchablePath = ref('');
|
||||
const searchableInputRef = ref();
|
||||
|
||||
watch(searchableStatus, (val) => {
|
||||
if (val) {
|
||||
searchablePath.value = paths.value.at(-1)?.url;
|
||||
nextTick(() => {
|
||||
searchableInputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
const searchableInputBlur = () => {
|
||||
searchableStatus.value = false;
|
||||
};
|
||||
|
||||
return {
|
||||
searchableStatus,
|
||||
searchablePath,
|
||||
searchableInputRef,
|
||||
searchableInputBlur,
|
||||
};
|
||||
}
|
@ -8,19 +8,31 @@
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<div class="path" ref="pathRef">
|
||||
<span ref="breadCrumbRef">
|
||||
<span class="root">
|
||||
<el-link @click="jump('/')">
|
||||
<el-icon :size="20"><HomeFilled /></el-icon>
|
||||
</el-link>
|
||||
<div v-show="!searchableStatus" tabindex="0" @click="searchableStatus = true">
|
||||
<div class="path" ref="pathRef">
|
||||
<span ref="breadCrumbRef">
|
||||
<span class="root">
|
||||
<el-link @click.stop="jump('/')">
|
||||
<el-icon :size="20"><HomeFilled /></el-icon>
|
||||
</el-link>
|
||||
</span>
|
||||
<span v-for="item in paths" :key="item.url" class="other">
|
||||
<span class="split">></span>
|
||||
<el-link @click.stop="jump(item.url)">{{ item.name }}</el-link>
|
||||
</span>
|
||||
</span>
|
||||
<span v-for="item in paths" :key="item.url" class="other">
|
||||
<span class="split">></span>
|
||||
<el-link @click="jump(item.url)">{{ item.name }}</el-link>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
ref="searchableInputRef"
|
||||
v-show="searchableStatus"
|
||||
v-model="searchablePath"
|
||||
@blur="searchableInputBlur"
|
||||
@keyup.enter="
|
||||
jump(searchablePath);
|
||||
searchableStatus = false;
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<LayoutContent :title="$t('file.file')" v-loading="loading">
|
||||
@ -175,6 +187,8 @@ import { useRouter } from 'vue-router';
|
||||
import { Back, Refresh } from '@element-plus/icons-vue';
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { useSearchable } from './hooks/searchable';
|
||||
import { ResultData } from '@/api/interface';
|
||||
|
||||
interface FilePaths {
|
||||
url: string;
|
||||
@ -184,7 +198,9 @@ interface FilePaths {
|
||||
const router = useRouter();
|
||||
const data = ref();
|
||||
let selects = ref<any>([]);
|
||||
let req = reactive({
|
||||
|
||||
// origin data
|
||||
const initData = () => ({
|
||||
path: '/',
|
||||
expand: true,
|
||||
showHidden: true,
|
||||
@ -193,6 +209,7 @@ let req = reactive({
|
||||
search: '',
|
||||
containSub: false,
|
||||
});
|
||||
let req = reactive(initData());
|
||||
let loading = ref(false);
|
||||
const paths = ref<FilePaths[]>([]);
|
||||
let pathWidth = ref(0);
|
||||
@ -223,6 +240,9 @@ const downloadRef = ref();
|
||||
const pathRef = ref();
|
||||
const breadCrumbRef = ref();
|
||||
|
||||
// editablePath
|
||||
const { searchableStatus, searchablePath, searchableInputRef, searchableInputBlur } = useSearchable(paths);
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 100,
|
||||
@ -235,15 +255,29 @@ const search = async () => {
|
||||
req.pageSize = paginationConfig.pageSize;
|
||||
await GetFilesList(req)
|
||||
.then((res) => {
|
||||
paginationConfig.total = res.data.itemTotal;
|
||||
data.value = res.data.items;
|
||||
req.path = res.data.path;
|
||||
handleSearchResult(res);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/** just search, no handleSearchResult */
|
||||
const searchFile = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
return await GetFilesList(req);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchResult = (res: ResultData<File.File>) => {
|
||||
paginationConfig.total = res.data.itemTotal;
|
||||
data.value = res.data.items;
|
||||
req.path = res.data.path;
|
||||
};
|
||||
|
||||
const open = async (row: File.File) => {
|
||||
if (row.isDir) {
|
||||
const name = row.name;
|
||||
@ -284,10 +318,18 @@ const back = () => {
|
||||
};
|
||||
|
||||
const jump = async (url: string) => {
|
||||
// reset search params before exec jump
|
||||
Object.assign(req, initData());
|
||||
req.path = url;
|
||||
req.containSub = false;
|
||||
req.search = '';
|
||||
await search();
|
||||
let searchResult = await searchFile();
|
||||
// check search result,the file is exists?
|
||||
if (!searchResult.data.path) {
|
||||
MsgWarning(i18n.global.t('commons.res.notFound'));
|
||||
return;
|
||||
}
|
||||
handleSearchResult(searchResult);
|
||||
getPaths(req.path);
|
||||
nextTick(function () {
|
||||
handlePath();
|
||||
@ -554,6 +596,10 @@ onMounted(() => {
|
||||
background-color: var(--panel-path-bg);
|
||||
height: 30px;
|
||||
border-radius: 2px !important;
|
||||
&:hover {
|
||||
cursor: text;
|
||||
box-shadow: var(--el-box-shadow);
|
||||
}
|
||||
|
||||
.root {
|
||||
vertical-align: middle;
|
||||
|
Loading…
Reference in New Issue
Block a user