feat: 新增选择文件上传功能,优化部分代码实现
This commit is contained in:
1 parent
60477a9112
commit
fda693326e
11 files changed
+292
-71
No files matched your search
@@ -1,8 +1,11 @@
|
||||
todo: 标签页,miaoDirectory切换动画 [finish]
|
||||
todo: 重构virtualPage,所有page以插件形式使用 [finish]
|
||||
todo: miaoDirectory插件化,为后续插件功能开发做准备 [finish]
|
||||
todo: miaoPopupInput, 弹出一个输入框
|
||||
todo: miaoDirectory的新建文件/文件夹功能
|
||||
todo: miaoDirectory的重命名功能
|
||||
todo: miaoDirectory的多选功能
|
||||
todo: miaoDirectory插件化,为后续插件功能开发做准备 [finish]
|
||||
todo: miaoPopupInput, 弹出一个输入框 [finish]
|
||||
todo: miaoDirectory的重命名功能 [finish]
|
||||
todo: miaoDirectory的剪切功能 [finish]
|
||||
todo: miaoDirectory的上传按钮 [finish]
|
||||
todo: VirtualDirectory更新时更新文件信息
|
||||
todo: miaoDirectory的新建文件/文件夹功能
|
||||
todo: miaoDirectory的复制文件/文件夹功能
|
||||
todo: miaoDirectory的多选功能
|
||||
@@ -1,6 +1,9 @@
|
||||
import generateId from "@/hooks/generateId";
|
||||
import useMiaoFetchApi from "@/hooks/useMiaoFetchApi";
|
||||
import type { stats, file, directory } from "@/types/type.ts";
|
||||
|
||||
const miaoFetchApi = useMiaoFetchApi();
|
||||
|
||||
export class VirtualFile {
|
||||
constructor(info: file, parent: VirtualDirectory) {
|
||||
this.name = info.name;
|
||||
@@ -127,5 +130,14 @@ class VirtualDirectory {
|
||||
return this.files?.includes(item) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用后重新向远端拉取文件夹信息并进行更新
|
||||
* @returns {void}
|
||||
*/
|
||||
async update(): Promise<void> {
|
||||
const data: (file | directory)[] = await miaoFetchApi.get(this);
|
||||
this.updateContent(data);
|
||||
}
|
||||
}
|
||||
export default VirtualDirectory;
|
||||
@@ -35,12 +35,12 @@ const onClickoutside = async () => {
|
||||
}
|
||||
|
||||
const handleMainMouseLeave = async () => {
|
||||
// setTimeout(() => {
|
||||
// showContextMenu.value -= 1
|
||||
// }, 200)
|
||||
nextTick(()=>{
|
||||
setTimeout(() => {
|
||||
showContextMenu.value -= 1
|
||||
})
|
||||
}, 100)
|
||||
// nextTick(()=>{
|
||||
// showContextMenu.value -= 1
|
||||
// })
|
||||
}
|
||||
|
||||
const handleSelect = async (key: string) => {
|
||||
|
||||
@@ -40,6 +40,9 @@ import {
|
||||
} from '@vicons/ionicons5'
|
||||
import { computed, ref } from 'vue'
|
||||
import dateFormatter from '@/hooks/dateFormatter'
|
||||
import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
|
||||
|
||||
const miaoFetchApi = useMiaoFetchApi()
|
||||
|
||||
const props = defineProps<{
|
||||
item: VirtualDirectory | VirtualFile
|
||||
@@ -50,6 +53,7 @@ const props = defineProps<{
|
||||
const emit = defineEmits<{
|
||||
download: []
|
||||
delete: []
|
||||
rename: []
|
||||
dragStart: [event: DragEvent, item: VirtualDirectory | VirtualFile]
|
||||
}>()
|
||||
|
||||
@@ -66,10 +70,14 @@ const time = computed(() => {
|
||||
|
||||
const dropDownOptions = computed(() => {
|
||||
const options = [
|
||||
{
|
||||
label: '重命名',
|
||||
key: 'rename'
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
key: 'delete'
|
||||
}
|
||||
},
|
||||
]
|
||||
if (itemType.value === 'file') {
|
||||
options.push(
|
||||
@@ -84,8 +92,11 @@ const dropDownOptions = computed(() => {
|
||||
return options
|
||||
})
|
||||
|
||||
// 是不是可以直接在这里面实现?
|
||||
const handleDropdownSelect = (key: string) => {
|
||||
if (key === 'download') {
|
||||
if (key === 'rename') {
|
||||
emit('rename')
|
||||
} else if (key === 'download') {
|
||||
emit('download')
|
||||
} else if (key === 'delete') {
|
||||
emit('delete')
|
||||
|
||||
@@ -24,6 +24,7 @@ const _resolve = ref()
|
||||
type popupConfigType = {
|
||||
title: string
|
||||
content?: string
|
||||
inputValue?: string,
|
||||
inputProps?: InputProps
|
||||
options: {
|
||||
label: string
|
||||
@@ -36,6 +37,7 @@ const popupConfig = ref<popupConfigType>()
|
||||
|
||||
const popup = async (config: popupConfigType) => {
|
||||
popupConfig.value = config
|
||||
inputValue.value = config.inputValue ?? ''
|
||||
showMask.value = true
|
||||
const key = await new Promise((resolve: (key: unknown) => unknown) => {
|
||||
_resolve.value = resolve
|
||||
|
||||
+3
-1
@@ -7,7 +7,9 @@ export default {
|
||||
get: '/get',
|
||||
upload: '/upload',
|
||||
delete: '/delete',
|
||||
query: '/query'
|
||||
query: '/query',
|
||||
rename: '/rename',
|
||||
cut: '/cut'
|
||||
},
|
||||
enableUpload: true,
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
export const filePicker = (() => {
|
||||
let _resolve: ((files: File[]) => void) | undefined;
|
||||
return (option?: { accept?: "string"; multiple: boolean }) => {
|
||||
if (_resolve) {
|
||||
_resolve([]);
|
||||
_resolve = undefined;
|
||||
}
|
||||
return new Promise<File[]>(resolve => {
|
||||
const inpEle = document.createElement("input");
|
||||
inpEle.id = `__file_${Math.ceil(Math.random() * 100000000)}`;
|
||||
inpEle.type = "file";
|
||||
inpEle.style.display = "none";
|
||||
_resolve = resolve;
|
||||
// 文件类型限制
|
||||
// inpEle.accept="image/*"
|
||||
option?.accept && (inpEle.accept = option?.accept);
|
||||
// 多选限制
|
||||
option?.multiple && (inpEle.multiple = option?.multiple);
|
||||
// inpEle.addEventListener("change", event => fn.call(inpEle, event, inpEle.files), { once: true });
|
||||
inpEle.addEventListener(
|
||||
"change",
|
||||
() => {
|
||||
if (!inpEle.files) {
|
||||
return;
|
||||
}
|
||||
const files: File[] = [];
|
||||
let i = 0;
|
||||
while (i < inpEle.files?.length) {
|
||||
const _file = inpEle?.files?.item(i);
|
||||
if (_file !== null) {
|
||||
files.push(_file);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
resolve(files);
|
||||
_resolve = undefined
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
inpEle.click();
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
export default {
|
||||
filePicker
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import VirtualDirectory, { VirtualFile } from "@/class/VirtualDirectory";
|
||||
import { getOption, getResponse, miaoFetchConfig, deleteOption, uploadOption, queryOption } from "@/types/fetch";
|
||||
import { getOption, getResponse, miaoFetchConfig, deleteOption, uploadOption, queryOption, renameOption, cutOption } from "@/types/fetch";
|
||||
import Config from "@/config";
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
|
||||
@@ -71,15 +71,15 @@ const miaoFetchApi = {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("file", data);
|
||||
// 必须这样传,不然中文乱码
|
||||
formData.append("fileName", data.name)
|
||||
// 必须这样传,不然中文乱码
|
||||
formData.append("fileName", data.name);
|
||||
formData.append("oprateType", opType);
|
||||
|
||||
return {
|
||||
abort: () => abortMiaoFetch(),
|
||||
response: new Promise<{
|
||||
message: 'success' | 'failed'
|
||||
error?: string
|
||||
message: "success" | "failed";
|
||||
error?: string;
|
||||
}>(async resolve => {
|
||||
const response = await miaoFetch({
|
||||
url,
|
||||
@@ -107,7 +107,7 @@ const miaoFetchApi = {
|
||||
|
||||
return {
|
||||
response: new Promise<{
|
||||
eventId: number
|
||||
eventId: number;
|
||||
}>(async resolve => {
|
||||
const response = await miaoFetch({
|
||||
url,
|
||||
@@ -117,6 +117,54 @@ const miaoFetchApi = {
|
||||
})
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 重命名对应的文件或文件夹
|
||||
*/
|
||||
rename(target: VirtualFile | VirtualDirectory, newName: string, option?: renameOption) {
|
||||
const { baseUrl, api } = Config;
|
||||
const url = `${baseUrl}${api.rename}${target.path}`;
|
||||
const retry = option?.retry ?? 0;
|
||||
const { miaoFetch } = useMiaoFetch({
|
||||
retry
|
||||
});
|
||||
|
||||
return {
|
||||
response: new Promise<{
|
||||
eventId: number;
|
||||
}>(async resolve => {
|
||||
const response = await miaoFetch({
|
||||
url,
|
||||
method: "post",
|
||||
data: { newName }
|
||||
});
|
||||
resolve(response.data);
|
||||
})
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 剪切文件或文件夹
|
||||
*/
|
||||
cut(target: VirtualFile | VirtualDirectory, to: VirtualDirectory, option?: cutOption) {
|
||||
const newPath = to.path
|
||||
const { baseUrl, api } = Config;
|
||||
const url = `${baseUrl}${api.cut}${target.path}`;
|
||||
const retry = option?.retry ?? 0;
|
||||
const { miaoFetch } = useMiaoFetch({
|
||||
retry
|
||||
});
|
||||
return {
|
||||
response: new Promise<{
|
||||
eventId: number;
|
||||
}>(async resolve => {
|
||||
const response = await miaoFetch({
|
||||
url,
|
||||
method: "post",
|
||||
data: { newPath }
|
||||
});
|
||||
resolve(response.data);
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询服务端操作事件的进展
|
||||
@@ -130,15 +178,15 @@ const miaoFetchApi = {
|
||||
});
|
||||
return {
|
||||
response: new Promise<{
|
||||
status: 'process' | 'success' | 'failed',
|
||||
error?: unknown
|
||||
status: "process" | "success" | "failed";
|
||||
error?: unknown;
|
||||
}>(async resolve => {
|
||||
const response = await miaoFetch({
|
||||
url,
|
||||
method: "get",
|
||||
params: {
|
||||
id: eventId
|
||||
}
|
||||
id: eventId
|
||||
}
|
||||
});
|
||||
resolve(response.data);
|
||||
})
|
||||
|
||||
+20
-21
@@ -1,29 +1,28 @@
|
||||
import { directory, file } from "./type";
|
||||
|
||||
export type miaoFetchConfig = {
|
||||
retry?: number
|
||||
}
|
||||
retry?: number;
|
||||
};
|
||||
|
||||
export type getOption = {
|
||||
} & miaoFetchConfig
|
||||
export type getOption = {} & miaoFetchConfig;
|
||||
|
||||
export type getResponse = (file | directory)[]
|
||||
export type getResponse = (file | directory)[];
|
||||
|
||||
export type uploadOption = {
|
||||
/**
|
||||
* 上传进度回调函数
|
||||
* @param {any} percent 进度百分比,float类型
|
||||
*/
|
||||
onProcess?: (percent: number) => void
|
||||
/**
|
||||
* 上传文件执行的操作
|
||||
* write即为覆盖写
|
||||
* append即为增量写
|
||||
*/
|
||||
type?: 'write' | 'append'
|
||||
} & miaoFetchConfig
|
||||
/**
|
||||
* 上传进度回调函数
|
||||
* @param {any} percent 进度百分比,float类型
|
||||
*/
|
||||
onProcess?: (percent: number) => void;
|
||||
/**
|
||||
* 上传文件执行的操作
|
||||
* write即为覆盖写
|
||||
* append即为增量写
|
||||
*/
|
||||
type?: "write" | "append";
|
||||
} & miaoFetchConfig;
|
||||
|
||||
export type deleteOption = {
|
||||
} & miaoFetchConfig
|
||||
export type queryOption = {
|
||||
} & miaoFetchConfig
|
||||
export type deleteOption = {} & miaoFetchConfig;
|
||||
export type renameOption = {} & miaoFetchConfig;
|
||||
export type cutOption = {} & miaoFetchConfig;
|
||||
export type queryOption = {} & miaoFetchConfig;
|
||||
+114
-27
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<miao-drop-handler @on-virtual-directory="handleDropVDirectory" @on-files="handleDropFiles">
|
||||
<miao-drop-handler @on-virtual-directory="handleDropVDirectory" @on-files="handleDropFiles"
|
||||
@on-virtual-files="handleDropVirtualFiles">
|
||||
<div class="miao-directory-item-container" ref="rootDomRef">
|
||||
<div class="container-top" :style="{
|
||||
backgroundColor:
|
||||
@@ -43,23 +44,25 @@
|
||||
<transition-group name="dirItem">
|
||||
<miaoDirectoryItem v-for="dir in showData_directory" @click="setCurrentDirectory(dir)"
|
||||
:item="dir" :key="dir.uid" :color="props.color" @delete="handleItemDelete(dir)"
|
||||
@drag-start="handleItemDragStart" />
|
||||
@drag-start="handleItemDragStart" @rename="handleItemRename(dir)" />
|
||||
</transition-group>
|
||||
<transition-group name="dirItem">
|
||||
<miaoDirectoryItem v-for="file in showData_files" :item="file" :key="file.uid"
|
||||
@click="openUrl(`${baseUrl}${api.get}${file.path}`)" @download="
|
||||
openUrl(`${baseUrl}${api.get}${file.path}`, {
|
||||
download: file.name
|
||||
})
|
||||
" @delete="handleItemDelete(file)" @drag-start="handleItemDragStart"
|
||||
:color="props.color" />
|
||||
@click="openUrl(`${baseUrl}${api.get}${file.path}`)" @download="handleItemDownload(file)"
|
||||
@delete="handleItemDelete(file)" @drag-start="handleItemDragStart"
|
||||
@rename="handleItemRename(file)" :color="props.color" />
|
||||
</transition-group>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
<div class="container-bottom" :style="{
|
||||
backgroundColor:
|
||||
index % 2 === 1 ? 'rgb(61 61 61)' : 'rgb(162 162 162)'
|
||||
}"></div>
|
||||
}">
|
||||
<div class="container-bottom-icon">
|
||||
<cloud-upload-outline
|
||||
:style="{ color: index % 2 === 0 ? 'rgb(0 0 0)' : 'rgb(255 255 255)' }" @click="handlePickFilesUpload"/>
|
||||
</div>
|
||||
</div>
|
||||
<miao-mask v-model:show="showModel"></miao-mask>
|
||||
<miao-popup-input ref="popupInput"></miao-popup-input>
|
||||
</div>
|
||||
@@ -82,10 +85,12 @@ import {
|
||||
CloudOutline,
|
||||
ChevronBackOutline,
|
||||
ReloadOutline,
|
||||
CloseOutline
|
||||
CloseOutline,
|
||||
CloudUploadOutline
|
||||
} from '@vicons/ionicons5'
|
||||
import useUploadQueue from '@/hooks/useUploadQueue'
|
||||
import useVirtualPages from '@/hooks/useVirtualPages'
|
||||
import { filePicker } from '@/hooks/miaoTools'
|
||||
|
||||
const { baseUrl, api } = Config
|
||||
const dataBus = useDataBus()
|
||||
@@ -195,6 +200,12 @@ const handleReload = () => {
|
||||
reload()
|
||||
}
|
||||
|
||||
const handleItemDownload = (item: VirtualFile) => {
|
||||
openUrl(`${baseUrl}${api.get}${item.path}`, {
|
||||
download: item.name
|
||||
})
|
||||
}
|
||||
|
||||
const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => {
|
||||
const { response } = miaoFetchApi.delete(item, {
|
||||
retry: 5
|
||||
@@ -208,6 +219,39 @@ const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleItemRename = async (item: VirtualDirectory | VirtualFile) => {
|
||||
const popupRes = await popupInput.value.popup({
|
||||
title: '重命名',
|
||||
showInput: true,
|
||||
inputValue: item.name,
|
||||
inputProps: {
|
||||
placeholder: item.name,
|
||||
},
|
||||
options: [{
|
||||
label: '取消',
|
||||
key: false,
|
||||
type: 'error'
|
||||
}, {
|
||||
label: '确认',
|
||||
key: true,
|
||||
type: 'success'
|
||||
},]
|
||||
})
|
||||
if (popupRes.key === false) {
|
||||
return
|
||||
}
|
||||
const newName = popupRes.text
|
||||
const { response } = miaoFetchApi.rename(item, newName, {
|
||||
retry: 5
|
||||
})
|
||||
const id = (await response).eventId
|
||||
const { response: res } = miaoFetchApi.query(id)
|
||||
const eventResult = await res
|
||||
if (eventResult.status === 'success') {
|
||||
reload()
|
||||
}
|
||||
}
|
||||
|
||||
const handleItemDragStart = async (
|
||||
_event: DragEvent,
|
||||
item: VirtualDirectory | VirtualFile
|
||||
@@ -232,26 +276,53 @@ const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
|
||||
if (vDirs[0].parent === currentDirectory.value) {
|
||||
return
|
||||
}
|
||||
const popupRes = await popupInput.value.popup({
|
||||
title: '重命名',
|
||||
showInput: true,
|
||||
inputProps: {
|
||||
placeholder: '',
|
||||
},
|
||||
options: [{
|
||||
label: '取消',
|
||||
key: false,
|
||||
type: 'error'
|
||||
}, {
|
||||
label: '确认',
|
||||
key: true,
|
||||
type: 'success'
|
||||
},]
|
||||
})
|
||||
console.log({ popupRes })
|
||||
for (let dir of vDirs) {
|
||||
; (async () => {
|
||||
const _from = dir.parent
|
||||
const { response } = miaoFetchApi.cut(dir, currentDirectory.value)
|
||||
const id = (await response).eventId
|
||||
const { response: res } = miaoFetchApi.query(id)
|
||||
const eventResult = await res
|
||||
if (eventResult.status === 'success') {
|
||||
reload()
|
||||
_from && _from.update()
|
||||
}
|
||||
})()
|
||||
}
|
||||
// setCurrentDirectory(vDirs[0])
|
||||
}
|
||||
|
||||
const handleDropVirtualFiles = (files: VirtualFile[]) => {
|
||||
if (files[0].parent === currentDirectory.value) {
|
||||
return
|
||||
}
|
||||
for (let file of files) {
|
||||
; (async () => {
|
||||
const _from = file.parent
|
||||
const { response } = miaoFetchApi.cut(file, currentDirectory.value)
|
||||
const id = (await response).eventId
|
||||
const { response: res } = miaoFetchApi.query(id)
|
||||
const eventResult = await res
|
||||
if (eventResult.status === 'success') {
|
||||
reload()
|
||||
_from.update()
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
const handlePickFilesUpload = async () => {
|
||||
const files = await filePicker({
|
||||
multiple: true
|
||||
})
|
||||
const _currDir = currentDirectory.value
|
||||
uploadQueue.push(files, _currDir, {
|
||||
onFinish(){
|
||||
_currDir.update()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await setCurrentDirectory(currentDirectory.value)
|
||||
})
|
||||
@@ -372,6 +443,22 @@ $bottom-bar-height: 60px;
|
||||
user-select: none;
|
||||
box-shadow: 0px 1px 10px #888888;
|
||||
z-index: 2;
|
||||
|
||||
.container-bottom-icon {
|
||||
svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,16 @@ export default defineConfig({
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/query/, "/query")
|
||||
},
|
||||
"/rename": {
|
||||
target: "http://127.0.0.1:17705",
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/rename/, "/rename")
|
||||
},
|
||||
"/cut": {
|
||||
target: "http://127.0.0.1:17705",
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/cut/, "/cut")
|
||||
},
|
||||
}
|
||||
},
|
||||
build: {
|
||||
|
||||
Reference in new issue
Block a user