This commit is contained in:
539943419 committed 2024-08-10 02:11:20 +08:00
1 parent 4ce50f697b
commit d9363d3b93
12 files changed
+610 -91

No files matched your search

+137 -12
View File
@@ -1,20 +1,145 @@
<script setup lang="ts">
import { NBreadcrumbItem, NBreadcrumb } from 'naive-ui'
import type { stats, file, directory } from '@/types/type.ts'
import VirtualDirectory from '@/class/virtualDirectory';
// import test from './hooks/test.ts';
import { onMounted, reactive } from 'vue';
import { NIcon } from 'naive-ui';
import { EyeOffOutline, EyeOutline, CloseOutline, AddOutline } from '@vicons/ionicons5'
import miaoDirectory from './views/miaoDirectory.vue';
import VirtualPage from './class/virtualPage';
const testData: string[] = ['111', '222', '333']
const views = reactive<VirtualPage[]>([])
const createView = () => {
views.push(reactive<VirtualPage>(new VirtualPage()))
}
const deleteView = (index: number) => {
views.splice(index, 1)
if(views.length === 0){
createView()
}
}
onMounted(() => {
createView()
})
</script>
<template>
<n-breadcrumb separator=">">
<n-breadcrumb-item v-for="(title, index) in testData">
{{ title }}
</n-breadcrumb-item>
</n-breadcrumb>
<div>miao</div>
<div class="view-controler">
<div class="tag" v-for="(view, index) of views" :key="view.uid">
<div class="tag-title">{{ view.title }}</div>
<div class="tag-control">
<n-icon class="tag-control-icon icon" @click="view.switchShow()">
<EyeOutline v-show="view.visible" class="icon-inner" />
<EyeOffOutline v-show="!view.visible" class="icon-inner" />
</n-icon>
<n-icon class="tag-control-icon icon" @click="deleteView(index)">
<CloseOutline class="icon-inner" />
</n-icon>
</div>
</div>
<div class="tag-short">
<n-icon class="icon icon-larger" @click="createView()" size="20">
<AddOutline class="icon-inner" />
</n-icon>
</div>
</div>
<div class="view-container">
<div class="view" v-for="(view) of views" v-show="view.visible" :key="view.uid">
<!-- title: {{ view.title }} -->
<miao-directory @on-title-change="(_t) => view.setTitle(_t)"></miao-directory>
</div>
</div>
</template>
<style scoped></style>
<style lang="scss" scoped>
$controler-height: 25px;
.view-controler {
display: flex;
height: $controler-height;
width: 100%;
overflow: hidden;
.tag-short {
position: relative;
// margin-left: 5px;
height: $controler-height;
width: $controler-height;
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;
display: flex;
align-items: center;
.tag-control-icon:not(:last-child) {
margin-right: 5px;
}
}
}
.icon {
height: calc($controler-height * 0.7);
width: calc($controler-height * 0.7);
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
background-color: rgba(128, 128, 128, 0);
transition: background-color 0.3s;
&:hover {
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;
height: calc(100% - $controler-height);
.view {
border: 1px solid black;
margin: 0 5px;
padding: 3px;
flex: 1;
}
}
</style>
+90 -44
View File
@@ -1,51 +1,97 @@
import type { stats, file, directory } from '@/types/type.ts'
import type { stats, file, directory } from "@/types/type.ts";
class VirtualFile {
constructor(info: file, parent: VirtualDirectory) {
this.name = info.name
this.size = info.size
this.stats = info.stats
this.parent = parent
}
name: string
size: number
stats: stats
parent: VirtualDirectory
get path() {
return `${this.parent.path}${this.name}`
}
constructor(info: file, parent: VirtualDirectory) {
this.name = info.name;
this.size = info.size;
this.stats = info.stats;
this.parent = parent;
this.type = "file";
}
name: string;
type: "file";
size: number;
stats: stats;
parent: VirtualDirectory;
get path() {
return `${this.parent.path}${this.name}`;
}
}
class VirtualDirectory {
constructor(info: directory, parent: VirtualDirectory) {
this.name = info.name
this.type = info.type
this.stats = info.stats
this.parent = parent ?? undefined
// 这里要改
this.content = info.content.map((v) => {
if (v.type === 'file') {
return new VirtualFile(v, this)
} else {
return new VirtualDirectory(v, this)
}
})
}
name: string
content: (VirtualFile | VirtualDirectory)[]
type: 'directory'
stats: stats
parent?: VirtualDirectory
constructor(info: directory, parent?: VirtualDirectory) {
this.name = info.name;
this.type = "directory";
this.stats = info.stats;
this.parent = parent ?? undefined;
// 这里要改
}
// 文件夹名
name: string;
// 子文件
files?: VirtualFile[];
// 子目录
directorys?: VirtualDirectory[];
// 没啥意义,标注一下就是了
type: "directory";
// 文件夹创建时间等杂项属性
stats: stats;
// 父文件夹
parent?: VirtualDirectory;
/**
* 相对于根目录的路径, 如: "/", "/123/321/"
* @returns {string}
*/
get path(): string {
if (!this.parent) {
return '/'
}
return `${this.parent.path}${this.name}/`
}
/**
* 获取相对于根目录的路径的层数
* 用于目录下文件定位
* @returns {string}
*/
get layer(): number {
return this.parent ? this.parent.layer + 1 : 0;
}
/**
* 相对于根目录的路径, 如: "/", "/123/321/"
* 用于目录下文件定位
* @returns {string}
*/
get path(): string {
if (!this.parent) {
return "/";
}
return `${this.parent.path}${this.name}/`;
}
/**
* 获取从根目录开始的按顺序的文件夹
* @returns {VirtualDirectory[]}
*/
get directoryArray(): VirtualDirectory[] {
if (!this.parent) {
return [this];
}
return [...this.parent.directoryArray, this];
}
/**
* 获取 layer 级父文件夹, 如getParent(1)就返回父级
* @param {number} layer 需要跳转的层数
* @returns {VirtualDirectory}
*/
getParent(layer: number): VirtualDirectory {
if (layer <= 0 || !this.parent) {
return this;
}
return this.parent.getParent(layer - 1);
}
setContent(content: (file | directory)[]) {
this.files = [];
this.directorys = [];
for (let item of content) {
if (item.type === "file") {
this.files.push(new VirtualFile(item, this));
} else if (item.type === "directory") {
this.directorys.push(new VirtualDirectory(item, this));
}
}
}
}
export default VirtualDirectory
export default VirtualDirectory;
+22
View File
@@ -0,0 +1,22 @@
import { ref, Ref } from "vue";
export default class VirtualPage {
constructor() {
this.visible = ref<boolean>(true);
this.title = ref<string>("");
this.uid = Math.ceil(Math.random() * 10000000000000);
}
visible: Ref<boolean>;
title: Ref<string>;
uid: number;
switchShow() {
// @ts-ignore 你只要知道这是对的就行了
this.visible = !this.visible;
}
setTitle(newTitle: string) {
// @ts-ignore 你只要知道这是对的就行了
this.title = newTitle;
}
}
+10
View File
@@ -0,0 +1,10 @@
<template>
<div class="container">
</div>
</template>
<script lang="ts" setup>
</script>
<style lang="scss" scoped></style>
+3 -2
View File
@@ -1,3 +1,4 @@
export default {
baseUrl: 'http://127.0.0.1:17705/'
}
baseUrl: "http://127.0.0.1:17705",
uploadSizeLimit: 1024 * 1024 * 100
};
+94
View File
@@ -0,0 +1,94 @@
import config from "@/config";
const { uploadSizeLimit } = config;
type dropHandlerHooks = {
onDrop?: (files: File[]) => void;
onEnter?: () => void;
/**
* 当拖动时
*/
onOver?: () => void;
onLeave?: () => void;
};
const isFile = (file: File) =>
new Promise(resolve => {
const reader = new FileReader();
reader.onload = () => {
resolve(true);
reader.abort();
};
reader.onerror = () => {
resolve(false);
reader.abort();
};
reader.readAsText(file);
});
/**
* 只保留文件,移去文件夹
* @param {File[]} files
*/
const filesFilter = async (files: File[]) => {
let index = 0;
while (index < files.length) {
const currentFile = files[index];
const _isFile = await isFile(currentFile);
console.log(_isFile);
if (_isFile) {
index += 1;
continue;
} else {
files.splice(index, 1);
continue;
}
}
};
export default function dropHandler(dom: HTMLDivElement, hooks?: dropHandlerHooks) {
console.log(dom);
dom.addEventListener(
"dragenter",
function (e) {
e.preventDefault();
e.stopPropagation();
hooks?.onEnter && hooks?.onEnter();
},
false
);
dom.addEventListener(
"dragover",
function (e) {
e.preventDefault();
e.stopPropagation();
hooks?.onOver && hooks?.onOver();
},
false
);
dom.addEventListener(
"dragleave",
function (e) {
e.preventDefault();
e.stopPropagation();
hooks?.onLeave && hooks?.onLeave();
},
false
);
dom.addEventListener(
"drop",
async function (e) {
// console.log(e, e.dataTransfer?.types);
e.preventDefault();
e.stopPropagation();
const files = [...(e.dataTransfer?.files ?? [])].filter(file => file.size < uploadSizeLimit);
await filesFilter(files);
hooks?.onDrop && hooks.onDrop(files);
},
false
);
}
+17 -16
View File
@@ -1,21 +1,22 @@
export type stats = {
atimeMs: number // 上次访问时间(毫秒)
birthtimeMs: number // 创建时间(毫秒)
ctimeMs: number // 状态更改时间(毫秒)
mtimeMs: number // 上次修改时间(毫秒)
}
atimeMs: number; // 上次访问时间(毫秒)
birthtimeMs: number; // 创建时间(毫秒)
ctimeMs: number; // 状态更改时间(毫秒)
mtimeMs: number; // 上次修改时间(毫秒)
};
export type file = {
name: string
size: number
type: 'file'
stats: stats
}
name: string;
size: number;
type?: "file";
stats: stats;
};
export type directory = {
name: string
content: (file | directory)[]
type: 'directory'
stats: stats
parent?: directory
}
name: string;
// 考虑到可能还没请求过来
content?: (file | directory)[];
type?: "directory";
stats: stats;
parent?: directory;
};
+72
View File
@@ -0,0 +1,72 @@
<template>
<div class="container" ref="rootDom">
<n-breadcrumb separator=">">
<n-breadcrumb-item v-for="(dir) in currentDirectory?.directoryArray" @click="setCurrentDirectory(dir)">
{{ dir.name }}
</n-breadcrumb-item>
</n-breadcrumb>
<button v-for="(dir) in currentDirectory?.directorys" @click="setCurrentDirectory(dir)" style="display: block;">
{{ `dir ===> ${dir.name}` }}
</button>
<a v-for="(file) in currentDirectory?.files" :href="`${baseUrl}/get${file.path}`" target="_blank" style="display: block;">
{{ `file ===> ${file.name}` }}
</a>
</div>
</template>
<script setup lang="ts">
import { NBreadcrumbItem, NBreadcrumb } from 'naive-ui'
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
}>()
const { baseUrl } = Config
const rootDom = ref<HTMLDivElement>()
const rootDirectory = new VirtualDirectory({
name: '根目录',
stats: {
atimeMs: 0,
birthtimeMs: 0,
ctimeMs: 0,
mtimeMs: 0,
}
})
const currentDirectory = ref<VirtualDirectory | undefined>(rootDirectory)
// 设置显示的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()
virtualDirectory.setContent(data)
}
// 刷新一下
currentDirectory.value = undefined
currentDirectory.value = virtualDirectory
emit("onTitleChange", currentDirectory.value.name)
}
onMounted(async () => {
await setCurrentDirectory(rootDirectory)
rootDom.value && dropHandler(rootDom.value, {
onDrop(files){
console.log(files, files.map(v=>v.name))
}
})
})
</script>
<style scoped lang="scss">
.container {
height: 100%;
width: 100%;
}
</style>