From a668b3b840ff4be684b1da60afc53e1503b8b9ca Mon Sep 17 00:00:00 2001 From: Miao_Spring <539943419@qq.com> Date: Sat, 10 Aug 2024 18:57:12 +0800 Subject: [PATCH] update --- src/App.vue | 201 +++++++++++------- src/class/virtualDirectory.ts | 38 +++- src/class/virtualPage.ts | 38 ++-- .../miaoDirectory/miaoDirectoryItem.vue | 121 +++++++++++ src/components/miaoDirectory/miaoHeader.vue | 10 + src/components/miaoHeader.vue | 10 - src/components/miaoMask.vue | 47 ++++ src/hooks/dateFormatter.ts | 20 ++ src/hooks/dropHandler.ts | 29 ++- src/hooks/generateId.ts | 16 ++ src/hooks/useDataBus.ts | 15 ++ src/hooks/useMiaoFetch.ts | 22 ++ src/views/miaoDirectory.vue | 132 ++++++++---- 13 files changed, 554 insertions(+), 145 deletions(-) create mode 100644 src/components/miaoDirectory/miaoDirectoryItem.vue create mode 100644 src/components/miaoDirectory/miaoHeader.vue delete mode 100644 src/components/miaoHeader.vue create mode 100644 src/components/miaoMask.vue create mode 100644 src/hooks/dateFormatter.ts create mode 100644 src/hooks/generateId.ts create mode 100644 src/hooks/useDataBus.ts create mode 100644 src/hooks/useMiaoFetch.ts diff --git a/src/App.vue b/src/App.vue index 8ac8090..ef7f8db 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,52 +1,75 @@ @@ -58,53 +81,70 @@ $controler-height: 25px; display: flex; height: $controler-height; width: 100%; - overflow: hidden; + background-color: #f2f2f2; + // overflow: hidden; - .tag-short { - position: relative; - // margin-left: 5px; - height: $controler-height; - width: $controler-height; + .tags-container { display: flex; - justify-content: center; - align-items: center; - } - - .tag { - position: relative; - width: 170px; - // flex: 1; - margin: 0 5px; - display: flex; - border-radius: 5px 5px 0 0; - align-items: center; - - &::after { - content: ''; - height: calc($controler-height*0.6); - width: 1px; - border-right: 1px solid black; - position: absolute; - right: 0px; - } - - .tag-title { - margin-left: 4px; - width: 70%; - user-select: none; - } - - .tag-control { - position: absolute; - right: 10px; + margin-left: 5px; + flex: 1; + // overflow-x: scroll; + .tag { + position: relative; + width: 170px; + margin: 0 5px; + // flex: 1; display: flex; + border-radius: 5px 5px 0 0; align-items: center; - - - .tag-control-icon:not(:last-child) { - margin-right: 5px; + .tag-title { + margin-left: 4px; + width: 70%; + user-select: none; } + + .tag-control { + position: absolute; + right: 10px; + display: flex; + align-items: center; + + .tag-control-icon:not(:last-child) { + margin-right: 5px; + } + } + + &:not(:last-child)::after { + content: ''; + height: calc($controler-height*0.6); + width: 1px; + border-right: 1px solid black; + position: absolute; + right: 0px; + } + } + } + + .view-controler-menu { + position: relative; + display: flex; + align-items: center; + padding: 0 5px; + .view-controler-menu-item { + height: 100%; + aspect-ratio: 1; + display: flex; + justify-content: center; + align-items: center; + } + &::before { + content: ''; + height: calc($controler-height*0.6); + width: 1px; + border-right: 1px solid black; + position: absolute; + left: 2px; } } @@ -123,23 +163,40 @@ $controler-height: 25px; background-color: rgba(128, 128, 128, 0.4); } } - - .icon-larger { - height: calc($controler-height * 0.85); - width: calc($controler-height * 0.85); - } } .view-container { position: relative; display: flex; + // flex-direction: column; height: calc(100% - $controler-height); + overflow-y: overlay; + overflow-x: overlay; + &::-webkit-scrollbar { + position: absolute; + width: 4px; + } + // 滚动条轨道 + &::-webkit-scrollbar-track { + // background: rgb(239, 239, 239); + border-radius: 2px; + } + // 小滑块 + &::-webkit-scrollbar-thumb { + background: #e0e0e0; + // border-radius: 5px; + } + &::-webkit-scrollbar-thumb:hover { + background: #999999; + } .view { - border: 1px solid black; - margin: 0 5px; - padding: 3px; + // border: 1px solid black; + // outline: 1px solid black; + // margin: 0 5px; + // padding: 3px; flex: 1; + height: 100%; } } diff --git a/src/class/virtualDirectory.ts b/src/class/virtualDirectory.ts index 6b7986a..2fbd0d1 100644 --- a/src/class/virtualDirectory.ts +++ b/src/class/virtualDirectory.ts @@ -1,18 +1,21 @@ +import generateId from "@/hooks/generateId"; import type { stats, file, directory } from "@/types/type.ts"; -class VirtualFile { +export class VirtualFile { constructor(info: file, parent: VirtualDirectory) { this.name = info.name; this.size = info.size; this.stats = info.stats; this.parent = parent; this.type = "file"; + this.uid = generateId() } name: string; type: "file"; size: number; stats: stats; parent: VirtualDirectory; + uid: number get path() { return `${this.parent.path}${this.name}`; } @@ -24,7 +27,7 @@ class VirtualDirectory { this.type = "directory"; this.stats = info.stats; this.parent = parent ?? undefined; - // 这里要改 + this.uid = generateId() } // 文件夹名 name: string; @@ -39,6 +42,8 @@ class VirtualDirectory { // 父文件夹 parent?: VirtualDirectory; + uid: number + /** * 获取相对于根目录的路径的层数 * 用于目录下文件定位 @@ -83,13 +88,34 @@ class VirtualDirectory { } setContent(content: (file | directory)[]) { - this.files = []; - this.directorys = []; + this.files = this.files ?? []; + this.directorys = this.directorys ?? []; + const fileNames:string[] = this.files.map(v=>v.name) + const dirNames:string[] = this.directorys.map(v=>v.name) + const itemNames:string[] = content.map(v=>v.name) + // 删去远程端已不存在的文件与文件夹 + const _delete = (target: (VirtualFile | VirtualDirectory)[]) => { + let index = 0 + while(index < target.length){ + if(!itemNames.includes(target[index].name)){ + target.splice(index, 1) + }else{ + index += 1 + } + } + } + _delete(this.files) + _delete(this.directorys) + // 加入新的文件与文件夹 for (let item of content) { if (item.type === "file") { - this.files.push(new VirtualFile(item, this)); + if(!fileNames.includes(item.name)){ + this.files.push(new VirtualFile(item, this)); + } } else if (item.type === "directory") { - this.directorys.push(new VirtualDirectory(item, this)); + if(!dirNames.includes(item.name)){ + this.directorys.push(new VirtualDirectory(item, this)); + } } } } diff --git a/src/class/virtualPage.ts b/src/class/virtualPage.ts index ce34a42..7601145 100644 --- a/src/class/virtualPage.ts +++ b/src/class/virtualPage.ts @@ -1,22 +1,36 @@ -import { ref, Ref } from "vue"; +import VirtualDirectory from "./virtualDirectory"; +import generateId from "@/hooks/generateId"; export default class VirtualPage { - constructor() { - this.visible = ref(true); - this.title = ref(""); - this.uid = Math.ceil(Math.random() * 10000000000000); + constructor(initDirectory: VirtualDirectory) { + this.visible = true; + this.uid = generateId() + this.currentDirectory = initDirectory + this.refreshKey = Math.random() } - visible: Ref; - title: Ref; + visible: boolean; + currentDirectory: VirtualDirectory; + // 区分每一个页面的唯一id uid: number; + // 当refreshKey改变时需要刷新页面,目前还没接入刷新 + refreshKey:number; + get title(){ + return this.currentDirectory.name + } + + // 切换显示状态 switchShow() { - // @ts-ignore 你只要知道这是对的就行了 this.visible = !this.visible; } - - setTitle(newTitle: string) { - // @ts-ignore 你只要知道这是对的就行了 - this.title = newTitle; + + // 设置(同步)正在展示的文件夹 + setCurrrentDirectory(currDir: VirtualDirectory) { + this.currentDirectory = currDir } + + refresh(){ + this.refreshKey = Math.random() + } + } diff --git a/src/components/miaoDirectory/miaoDirectoryItem.vue b/src/components/miaoDirectory/miaoDirectoryItem.vue new file mode 100644 index 0000000..f560203 --- /dev/null +++ b/src/components/miaoDirectory/miaoDirectoryItem.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/src/components/miaoDirectory/miaoHeader.vue b/src/components/miaoDirectory/miaoHeader.vue new file mode 100644 index 0000000..f8a2d5a --- /dev/null +++ b/src/components/miaoDirectory/miaoHeader.vue @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/src/components/miaoHeader.vue b/src/components/miaoHeader.vue deleted file mode 100644 index 32bdeb0..0000000 --- a/src/components/miaoHeader.vue +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/miaoMask.vue b/src/components/miaoMask.vue new file mode 100644 index 0000000..72f3796 --- /dev/null +++ b/src/components/miaoMask.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/hooks/dateFormatter.ts b/src/hooks/dateFormatter.ts new file mode 100644 index 0000000..f32f9dc --- /dev/null +++ b/src/hooks/dateFormatter.ts @@ -0,0 +1,20 @@ +export default function dateFormatter(date: Date, fmt: string) { + var o:any = { + "M+": date.getMonth() + 1, //月份 + "d+": date.getDate(), //日 + "h+": date.getHours(), //小时 + "m+": date.getMinutes(), //分 + "s+": date.getSeconds(), //秒 + "q+": Math.floor((date.getMonth() + 3) / 3), //季度 + "S": date.getMilliseconds() //毫秒 + }; + if (/(y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); + } + for (var k in o) { + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); + } + } + return fmt; +} diff --git a/src/hooks/dropHandler.ts b/src/hooks/dropHandler.ts index 638c1bd..c17e1d0 100644 --- a/src/hooks/dropHandler.ts +++ b/src/hooks/dropHandler.ts @@ -2,14 +2,30 @@ import config from "@/config"; const { uploadSizeLimit } = config; -type dropHandlerHooks = { - onDrop?: (files: File[]) => void; - onEnter?: () => void; +export type dropHandlerHooks = { + /** + * 仅返回小于config.ts中大小限制的文件,不会返回文件夹 + */ + onDropFiles?: (files: File[]) => void; /** * 当拖动时 */ onOver?: () => void; + /** + * 当进入时 + */ + onEnter?: () => void; + + /** + * 当离开时 + */ onLeave?: () => void; + + /** + * 离开或是放下时都会触发 + * @returns {any} + */ + onEnd?: () => void; }; const isFile = (file: File) => @@ -26,7 +42,6 @@ const isFile = (file: File) => reader.readAsText(file); }); - /** * 只保留文件,移去文件夹 * @param {File[]} files @@ -36,7 +51,6 @@ const filesFilter = async (files: File[]) => { while (index < files.length) { const currentFile = files[index]; const _isFile = await isFile(currentFile); - console.log(_isFile); if (_isFile) { index += 1; continue; @@ -48,7 +62,6 @@ const filesFilter = async (files: File[]) => { }; export default function dropHandler(dom: HTMLDivElement, hooks?: dropHandlerHooks) { - console.log(dom); dom.addEventListener( "dragenter", function (e) { @@ -75,6 +88,7 @@ export default function dropHandler(dom: HTMLDivElement, hooks?: dropHandlerHook e.preventDefault(); e.stopPropagation(); hooks?.onLeave && hooks?.onLeave(); + hooks?.onEnd && hooks.onEnd(); }, false ); @@ -87,7 +101,8 @@ export default function dropHandler(dom: HTMLDivElement, hooks?: dropHandlerHook e.stopPropagation(); const files = [...(e.dataTransfer?.files ?? [])].filter(file => file.size < uploadSizeLimit); await filesFilter(files); - hooks?.onDrop && hooks.onDrop(files); + hooks?.onDropFiles && hooks.onDropFiles(files); + hooks?.onEnd && hooks.onEnd(); }, false ); diff --git a/src/hooks/generateId.ts b/src/hooks/generateId.ts new file mode 100644 index 0000000..ff0fb08 --- /dev/null +++ b/src/hooks/generateId.ts @@ -0,0 +1,16 @@ +const idList: number[] = []; + +/** + * 生成一个唯一的id + * @param {any} length 生成id的长度 + * @returns {number} + */ +export default function generateId(length?: number) { + length || (length = 16); + let id; + while (!id || idList.includes(id)) { + id = Math.ceil(Math.random() * 10 ** 16); + } + idList.push(id) + return id +} diff --git a/src/hooks/useDataBus.ts b/src/hooks/useDataBus.ts new file mode 100644 index 0000000..30721cc --- /dev/null +++ b/src/hooks/useDataBus.ts @@ -0,0 +1,15 @@ +const dataStorage: any = {}; + +export default function useDataBus() { + return { + set(key: string | number, value: any) { + dataStorage[key] = value; + return value; + }, + get(key: string | number) { + const value = dataStorage[key]; + dataStorage[key] = undefined; + return value; + } + }; +} diff --git a/src/hooks/useMiaoFetch.ts b/src/hooks/useMiaoFetch.ts new file mode 100644 index 0000000..f82f5cc --- /dev/null +++ b/src/hooks/useMiaoFetch.ts @@ -0,0 +1,22 @@ +type miaoFetchConfig = { + retry: number; +}; + +export default function useMiaoFetch(config: miaoFetchConfig) { + const { retry } = config; + + return async (input: RequestInfo | URL, init?: RequestInit) => { + let retryTimes = 0; + let err: any; + while (retryTimes <= retry) { + try { + const response = await fetch(input, init); + return response; + } catch (e) { + err = e; + retryTimes += 1; + } + } + throw err; + }; +} diff --git a/src/views/miaoDirectory.vue b/src/views/miaoDirectory.vue index b63d019..81989a7 100644 --- a/src/views/miaoDirectory.vue +++ b/src/views/miaoDirectory.vue @@ -1,16 +1,23 @@ @@ -20,53 +27,102 @@ import type { file, directory } from '@/types/type.ts' import VirtualDirectory from '@/class/virtualDirectory'; import { onMounted, ref } from 'vue'; import Config from '@/config' -import dropHandler from '@/hooks/dropHandler'; - -const emit = defineEmits<{ - (e: 'onTitleChange', title: string): void -}>() +import dropHandler, { dropHandlerHooks } from '@/hooks/dropHandler'; +import miaoMask from '@/components/miaoMask.vue'; +import miaoDirectoryItem from '@/components/miaoDirectory/miaoDirectoryItem.vue'; +import useMiaoFetch from '@/hooks/useMiaoFetch'; +import useDataBus from '@/hooks/useDataBus'; const { baseUrl } = Config +const dataBus = useDataBus() -const rootDom = ref() - -const rootDirectory = new VirtualDirectory({ - name: '根目录', - stats: { - atimeMs: 0, - birthtimeMs: 0, - ctimeMs: 0, - mtimeMs: 0, - } +const miaoFetch = useMiaoFetch({ + retry: 5, }) -const currentDirectory = ref(rootDirectory) +// 目前展示的Dir +const currentDirectory = defineModel('currentDirectory', { required: true }) + +// 是否显示模态框 +const showModel = ref(0) + +// 用于监听拖拽文件事件 +const rootDomRef = ref() // 设置显示的dir const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => { - if (virtualDirectory.files === undefined && virtualDirectory.directorys === undefined) { - const data: (file | directory)[] = await (await fetch(`${baseUrl}/get${virtualDirectory.path}`)).json() + // 若没有缓存数据则同步刷新,有缓存数据则异步刷新 + if (virtualDirectory.files === undefined || virtualDirectory.directorys === undefined) { + const data: (file | directory)[] = await (await miaoFetch(`${baseUrl}/get${virtualDirectory.path}`)).json() virtualDirectory.setContent(data) + } else { + ; (async () => { + const data: (file | directory)[] = await (await miaoFetch(`${baseUrl}/get${virtualDirectory.path}`)).json() + virtualDirectory.setContent(data) + currentDirectory.value = undefined + currentDirectory.value = virtualDirectory + })() } // 刷新一下 currentDirectory.value = undefined currentDirectory.value = virtualDirectory - emit("onTitleChange", currentDirectory.value.name) + // emit("onCurrentDirectoryChange", currentDirectory.value) +} + +const openUrl = (href: string) => { + var oA = document.createElement("a"); + oA.href = href; + oA.target = "_blank"; + oA.click(); } onMounted(async () => { - await setCurrentDirectory(rootDirectory) - rootDom.value && dropHandler(rootDom.value, { - onDrop(files){ - console.log(files, files.map(v=>v.name)) - } - }) + currentDirectory.value && await setCurrentDirectory(currentDirectory.value) + // 拖动事件处理 + const dropFileOption:dropHandlerHooks = { + onDropFiles(files: File[]) { + const dragData = dataBus.get('dragData') + console.log({dragData, files}) + }, + onEnter() { + showModel.value += 1 + }, + onEnd() { + showModel.value -= 1 + }, + } + rootDomRef.value && dropHandler(rootDomRef.value, dropFileOption) })