Compare commits

2 Commits
7 changed files with 463 additions and 224 deletions

No files matched your search

+92 -43
View File
@@ -1,14 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { h, onMounted, reactive, ref, shallowRef } from 'vue'
import { h, onMounted, reactive, ref, shallowRef } from 'vue'; import { NIcon, NScrollbar, NDropdown, NQrCode } from 'naive-ui'
import { NIcon, NScrollbar, NDropdown, NQrCode } from 'naive-ui'; import {
import { EyeOffOutline, EyeOutline, CloseOutline, CopyOutline, EllipsisVertical, QrCodeOutline } from '@vicons/ionicons5' EyeOffOutline,
import miaoDirectory from './views/miaoDirectory.vue'; EyeOutline,
import VirtualPage from './class/VirtualPage'; CloseOutline,
import VirtualDirectory, { VirtualFile } from './class/VirtualDirectory'; CopyOutline,
import MiaoMask from './components/miaoMask.vue'; EllipsisVertical,
import config from './config'; QrCodeOutline
import useViretualPages from './hooks/useVirtualPages'; } from '@vicons/ionicons5'
import miaoDirectory from './views/miaoDirectory.vue'
import VirtualPage from './class/VirtualPage'
import VirtualDirectory, { VirtualFile } from './class/VirtualDirectory'
import MiaoMask from './components/miaoMask.vue'
import config from './config'
import useVirtualPages from './hooks/useVirtualPages'
const { baseUrl } = config const { baseUrl } = config
@@ -19,22 +25,32 @@ const modalData = shallowRef<{
props: any props: any
}>() }>()
const views = useViretualPages() const views = useVirtualPages()
const rootDirectory = reactive(new VirtualDirectory({ const rootDirectory = reactive(
new VirtualDirectory({
name: '根目录', name: '根目录',
stats: { stats: {
atimeMs: 0, atimeMs: 0,
birthtimeMs: 0, birthtimeMs: 0,
ctimeMs: 0, ctimeMs: 0,
mtimeMs: 0, mtimeMs: 0
} }
})) })
)
// component就是一个vue组件,类似于miaoDirectory // component就是一个vue组件,类似于miaoDirectory
const createView = (component: any, currentObjects: (VirtualDirectory | VirtualFile)[], index?: number) => { const createView = (
component: any,
currentObjects: (VirtualDirectory | VirtualFile)[],
index?: number
) => {
index ?? (index = views.length) index ?? (index = views.length)
views.splice(index, 0, reactive<VirtualPage>(new VirtualPage(component, [...currentObjects]))) views.splice(
index,
0,
reactive<VirtualPage>(new VirtualPage(component, [...currentObjects]))
)
} }
const deleteView = (index: number) => { const deleteView = (index: number) => {
@@ -46,7 +62,7 @@ const deleteView = (index: number) => {
// 点击标题是隐藏其他标签或显示其他标签,还挺好用的 // 点击标题是隐藏其他标签或显示其他标签,还挺好用的
const handleClickTitle = (index: number) => { const handleClickTitle = (index: number) => {
let count = views.filter(v => v.visible).length let count = views.filter((v) => v.visible).length
let op = !views[index].visible || count > 1 ? 'hideOthers' : 'showOthers' let op = !views[index].visible || count > 1 ? 'hideOthers' : 'showOthers'
let _index = 0 let _index = 0
while (_index < views.length) { while (_index < views.length) {
@@ -59,7 +75,6 @@ const handleClickTitle = (index: number) => {
} }
} }
// 顶部菜单相关 // 顶部菜单相关
function renderIcon(icon: any) { function renderIcon(icon: any) {
return () => { return () => {
@@ -102,19 +117,43 @@ onMounted(() => {
<n-scrollbar x-scrollable> <n-scrollbar x-scrollable>
<div class="tags-container"> <div class="tags-container">
<transition-group name="tab"> <transition-group name="tab">
<div class="tab" v-for="(view, index) of views" :key="view.uid"> <div
<div class="tab-title" @click="handleClickTitle(index)">{{ view.title }}</div> class="tab"
<div class="tab-point" :style="{ backgroundColor: view.color }"></div> v-for="(view, index) of views"
:key="view.uid">
<div
class="tab-title"
@click="handleClickTitle(index)">
{{ view.title }}
</div>
<div
class="tab-point"
:style="{ backgroundColor: view.color }"></div>
<div class="tab-control"> <div class="tab-control">
<n-icon class="tag-control-icon icon" @click="view.switchShow()"> <n-icon
<EyeOutline v-show="view.visible" class="icon-inner" /> class="tag-control-icon icon"
<EyeOffOutline v-show="!view.visible" class="icon-inner" /> @click="view.switchShow()">
<EyeOutline
v-show="view.visible"
class="icon-inner" />
<EyeOffOutline
v-show="!view.visible"
class="icon-inner" />
</n-icon> </n-icon>
<n-icon class="tag-control-icon icon" <n-icon
@click="createView(view.component, view.currentObjects, index + 1)"> class="tag-control-icon icon"
@click="
createView(
view.component,
view.currentObjects,
index + 1
)
">
<CopyOutline /> <CopyOutline />
</n-icon> </n-icon>
<n-icon class="tag-control-icon icon" @click="deleteView(index)"> <n-icon
class="tag-control-icon icon"
@click="deleteView(index)">
<CloseOutline class="icon-inner" /> <CloseOutline class="icon-inner" />
</n-icon> </n-icon>
</div> </div>
@@ -122,9 +161,12 @@ onMounted(() => {
</transition-group> </transition-group>
</div> </div>
</n-scrollbar> </n-scrollbar>
<div class="view-controler-menu"> <div class="view-controller-menu">
<div class="view-controler-menu-item"> <div class="view-controller-menu-item">
<n-dropdown trigger="click" :options="openMenuOption" @select="handleMenuSelect"> <n-dropdown
trigger="click"
:options="openMenuOption"
@select="handleMenuSelect">
<n-icon class="icon"> <n-icon class="icon">
<EllipsisVertical class="icon-inner" /> <EllipsisVertical class="icon-inner" />
</n-icon> </n-icon>
@@ -134,16 +176,26 @@ onMounted(() => {
</div> </div>
<div class="view-container"> <div class="view-container">
<transition-group name="page"> <transition-group name="page">
<div class="view-container-item" v-for="(view, index) of views" v-show="view.visible" :key="view.uid"> <div
<component :is="view.component" v-model:current-objects="view.currentObjects" :id="view.uid" class="view-container-item"
:color="view.color" @exit="deleteView(index)"></component> v-for="(view, index) of views"
v-show="view.visible"
:key="view.uid">
<component
:is="view.component"
v-model:current-objects="view.currentObjects"
:id="view.uid"
:color="view.color"
@exit="deleteView(index)"></component>
</div> </div>
</transition-group> </transition-group>
</div> </div>
</div> </div>
<!-- 模态框展示,用来显示分享二维码,设置菜单之类的东西 --> <!-- 模态框展示,用来显示分享二维码,设置菜单之类的东西 -->
<MiaoMask v-model:show="showModal" @click="showModal = false"> <MiaoMask v-model:show="showModal" @click="showModal = false">
<component :is="modalData?.component" v-bind="modalData?.props"></component> <component
:is="modalData?.component"
v-bind="modalData?.props"></component>
</MiaoMask> </MiaoMask>
</template> </template>
@@ -205,7 +257,7 @@ $tag-width: 170px;
&:not(:last-child)::after { &:not(:last-child)::after {
content: ''; content: '';
height: calc($controler-height*0.6); height: calc($controler-height * 0.6);
width: 1px; width: 1px;
border-right: 1px solid black; border-right: 1px solid black;
position: absolute; position: absolute;
@@ -214,13 +266,13 @@ $tag-width: 170px;
} }
} }
.view-controler-menu { .view-controller-menu {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 5px; padding: 0 5px;
.view-controler-menu-item { .view-controller-menu-item {
height: 100%; height: 100%;
aspect-ratio: 1; aspect-ratio: 1;
display: flex; display: flex;
@@ -230,7 +282,7 @@ $tag-width: 170px;
&::before { &::before {
content: ''; content: '';
height: calc($controler-height*0.6); height: calc($controler-height * 0.6);
width: 1px; width: 1px;
border-right: 1px solid black; border-right: 1px solid black;
position: absolute; position: absolute;
@@ -295,7 +347,6 @@ $tag-width: 170px;
.view { .view {
.view-controler { .view-controler {
.tags-container { .tags-container {
.tab-move, .tab-move,
.tab-enter-active { .tab-enter-active {
transition: all 0.2s ease; transition: all 0.2s ease;
@@ -321,13 +372,11 @@ $tag-width: 170px;
} }
} }
.view-container { // .view-container {
// .page-move { // .page-move {
// transition: all 0.2s ease; // transition: all 0.2s ease;
// } // }
// .page-enter-active { // .page-enter-active {
// } // }
@@ -343,6 +392,6 @@ $tag-width: 170px;
// .page-leave-active { // .page-leave-active {
// position: absolute; // position: absolute;
// } // }
} // }
} }
</style> </style>
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,13 @@
<template> <template>
<div class="miao-item" draggable="true" ref="itemRef" @dragstart="handleDragStart"> <div
class="miao-item"
draggable="true"
ref="itemRef"
@dragstart="handleDragStart">
<div class="item-main"> <div class="item-main">
<div class="item-main-front" :style="{ backgroundColor: props.color }"></div> <div
class="item-main-front"
:style="{ backgroundColor: props.color }"></div>
<Icon class="item-main-icon"></Icon> <Icon class="item-main-icon"></Icon>
<div class="item-main-info"> <div class="item-main-info">
<div class="item-main-info-name"> <div class="item-main-info-name">
@@ -11,8 +17,13 @@
{{ time }} {{ time }}
</div> </div>
</div> </div>
<n-dropdown trigger="click" :options="dropDownOptions" @select="handleDropdownSelect"> <n-dropdown
<div class="item-main-option" @click="e=>e.stopPropagation()"> trigger="click"
:options="dropDownOptions"
@select="handleDropdownSelect">
<div
class="item-main-option"
@click="(e) => e.stopPropagation()">
<EllipsisVertical class="item-main-option-icon" /> <EllipsisVertical class="item-main-option-icon" />
</div> </div>
</n-dropdown> </n-dropdown>
@@ -21,96 +32,138 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'
import {
DocumentText,
FileTray,
Help,
Image,
LogoChrome,
LogoCss3,
LogoJavascript,
LogoVue,
PlayCircle,
EllipsisVertical,
Disc
} from '@vicons/ionicons5'
import { computed, ref } from 'vue'
import dateFormatter from '@/hooks/dateFormatter'
// import useDataBus from '@/hooks/useDataBus'
import { NDropdown } from 'naive-ui'
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'; // const dataBus = useDataBus()
import { DocumentText, FileTray, Help, Image, LogoChrome, LogoCss3, LogoJavascript, LogoVue, PlayCircle, EllipsisVertical, Disc } from '@vicons/ionicons5';
import { computed, ref } from 'vue';
import dateFormatter from '@/hooks/dateFormatter';
import useDataBus from '@/hooks/useDataBus';
import { NDropdown } from 'naive-ui';
const dataBus = useDataBus()
const emit = defineEmits<{
download: []
delete: []
dragStart: []
}>()
const props = defineProps<{ const props = defineProps<{
item: VirtualDirectory | VirtualFile item: VirtualDirectory | VirtualFile
name?: string name?: string
color:string color: string
}>()
const emit = defineEmits<{
download: []
delete: []
dragStart: [event: DragEvent, item: VirtualDirectory | VirtualFile]
}>() }>()
const itemType = ref<'file' | 'directory'>(props.item.type) const itemType = ref<'file' | 'directory'>(props.item.type)
const name = props.name ?? props.item.name const name = props.name ?? props.item.name
const time = computed(()=>{ const time = computed(() => {
return dateFormatter(new Date(props.item.stats.mtimeMs), "yyyy-MM-dd hh:mm:ss"); return dateFormatter(
new Date(props.item.stats.mtimeMs),
'yyyy-MM-dd hh:mm:ss'
)
}) })
const dropDownOptions = computed(()=> { const dropDownOptions = computed(() => {
const options = [{ const options = [
{
label: '删除', label: '删除',
key: 'delete' key: 'delete'
}] }
if(itemType.value === 'file'){ ]
options.push(...[ if (itemType.value === 'file') {
options.push(
...[
{ {
label: '下载', label: '下载',
key: 'download', key: 'download'
} }
]) ]
)
} }
return options return options
}) })
const handleDropdownSelect = (key: 'download' | 'delete') => { const handleDropdownSelect = (key: 'download' | 'delete') => {
if(key === 'download'){ if (key === 'download') {
emit('download') emit('download')
} else if (key === 'delete') { } else if (key === 'delete') {
emit('delete') emit('delete')
} }
} }
const Icon = computed(()=>{ const Icon = computed(() => {
try{ try {
if(props.item.type === 'directory') { if (props.item.type === 'directory') {
return FileTray return FileTray
} else if(props.item.type === 'file') { } else if (props.item.type === 'file') {
const aft = props.item.name.split('.').at(-1)?.toLowerCase() const aft = props.item.name.split('.').at(-1)?.toLowerCase()
if(!aft){ if (!aft) {
throw(1) throw 1
}else if(['txt', 'doc', 'docs', 'docx', 'xls', 'xlsx'].includes(aft)){ } else if (
['txt', 'doc', 'docs', 'docx', 'xls', 'xlsx'].includes(aft)
) {
return DocumentText return DocumentText
}else if(['mp3', 'wav', 'ape', 'acc', 'ogg', 'flac'].includes(aft)){ } else if (
['mp3', 'wav', 'ape', 'acc', 'ogg', 'flac'].includes(aft)
) {
return Disc return Disc
}else if(['.xbm','.tif','pjp','.svgz','jpg','jpeg','ico','tiff','.gif','svg','.jfif','.webp','.png','.bmp','pjpeg','.avif'].includes(aft)){ } else if (
[
'.xbm',
'.tif',
'pjp',
'.svgz',
'jpg',
'jpeg',
'ico',
'tiff',
'.gif',
'svg',
'.jfif',
'.webp',
'.png',
'.bmp',
'pjpeg',
'.avif'
].includes(aft)
) {
return Image return Image
}else if(['mp4', 'm2v', 'mkv'].includes(aft)){ } else if (['mp4', 'm2v', 'mkv'].includes(aft)) {
return PlayCircle return PlayCircle
}else if(['js', 'jsx', 'ts', 'tsx'].includes(aft)){ } else if (['js', 'jsx', 'ts', 'tsx'].includes(aft)) {
return LogoJavascript return LogoJavascript
}else if(['html'].includes(aft)){ } else if (['html'].includes(aft)) {
return LogoChrome return LogoChrome
}else if(['css', 'scss', 'less'].includes(aft)){ } else if (['css', 'scss', 'less'].includes(aft)) {
return LogoCss3 return LogoCss3
}else if(['vue'].includes(aft)){ } else if (['vue'].includes(aft)) {
return LogoVue return LogoVue
} }
return DocumentText return DocumentText
} }
}catch{ } catch {
return Help return Help
} }
}) })
const itemRef = ref() const itemRef = ref()
const handleDragStart = () => { const handleDragStart = (event: DragEvent) => {
dataBus.set('dragData', [ props.item ]) emit('dragStart', event, props.item)
// dataBus.set('dragData', [props.item])
} }
</script> </script>
@@ -138,7 +191,7 @@ const handleDragStart = () => {
.item-main-front { .item-main-front {
height: 100%; height: 100%;
width: 10px; width: 10px;
border-radius: 7px 0 0 7px ; border-radius: 7px 0 0 7px;
background-color: rgb(0, 0, 0); background-color: rgb(0, 0, 0);
} }
.item-main-icon { .item-main-icon {
@@ -176,17 +229,18 @@ const handleDragStart = () => {
color: #000; color: #000;
transition: color 0.15s; transition: color 0.15s;
} }
&:hover, &:active { &:hover,
&:active {
background-color: #8b8b8b; background-color: #8b8b8b;
.item-main-option-icon { .item-main-option-icon {
color: #FFF; color: #fff;
} }
} }
} }
&:hover, &:active { &:hover,
&:active {
background-color: #d2d2d2; background-color: #d2d2d2;
} }
} }
} }
</style> </style>
+106
View File
@@ -0,0 +1,106 @@
<template>
<div
class="container"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
@drop="handleDrop">
<slot></slot>
<miao-mask v-model:show="showMask"></miao-mask>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import miaoMask from './miaoMask.vue'
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'
import config from '@/config'
import useDataBus from '@/hooks/useDataBus'
const dataBus = useDataBus()
const { uploadSizeLimit } = config
const emit = defineEmits<{
onFiles: [files: File[]]
onVirtualFiles: [vFiles: VirtualFile[]]
onVirtualDirectory: [vDirectory: VirtualDirectory[]]
}>()
const showMask = ref<number>(0)
// File類型的數據可能爲文件夾,通過FileReader進行判斷
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 removeDirectory = async (files: File[]) => {
let index = 0
while (index < files.length) {
const currentFile = files[index]
const _isFile = await isFile(currentFile)
if (_isFile) {
index += 1
continue
} else {
files.splice(index, 1)
continue
}
}
}
const handleDragOver = async (event: DragEvent) => {
showMask.value += 1
event.preventDefault()
}
const handleDragLeave = async () => {
showMask.value = 0
}
const handleDrop = async (event: DragEvent) => {
showMask.value = 0
event.preventDefault()
event.stopPropagation()
// todo: uploadSize的限制不應該在這裏
const files = [...(event.dataTransfer?.files ?? [])].filter(
(file) => file.size < uploadSizeLimit
)
await removeDirectory(files)
const virtualFiles: VirtualFile[] = dataBus.pop('dragData_vFiles')
const virtualDirectory: VirtualDirectory[] = dataBus.pop(
'dragData_vDirectory'
)
// console.log({ virtualDirectory, virtualFiles })
if (files && files.length) {
emit('onFiles', files)
}
if (virtualFiles && virtualFiles.length) {
emit('onVirtualFiles', virtualFiles)
}
if (virtualDirectory && virtualDirectory.length) {
emit('onVirtualDirectory', virtualDirectory)
}
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
position: relative;
}
</style>
+1 -1
View File
@@ -2,7 +2,7 @@
<transition name="transition"> <transition name="transition">
<div class="mask-body" ref="maskBodyRef" v-show="showMask"> <div class="mask-body" ref="maskBodyRef" v-show="showMask">
<div @click="e=>e.stopPropagation()"> <div @click="e=>e.stopPropagation()">
<slot> {{}} </slot> <slot></slot>
</div> </div>
</div> </div>
</transition> </transition>
+159 -129
View File
@@ -1,10 +1,15 @@
<template> <template>
<miao-drop-handler @on-virtual-directory="handleDropVDirectory" @on-files="handleDropFiles">
<div class="miao-directory-item-container" ref="rootDomRef"> <div class="miao-directory-item-container" ref="rootDomRef">
<div <div
class="container-top" class="container-top"
:style="{ backgroundColor: index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)' }" :style="{
> backgroundColor:
<div class="container-top-colorbar" :style="{ backgroundColor: props.color }"></div> index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)'
}">
<div
class="container-top-colorfulBar"
:style="{ backgroundColor: props.color }"></div>
<div class="container-top-breadcrumb"> <div class="container-top-breadcrumb">
<!-- <n-scrollbar x-scrollable> --> <!-- <n-scrollbar x-scrollable> -->
<div class="container-top-breadcrumb-container"> <div class="container-top-breadcrumb-container">
@@ -17,8 +22,7 @@
<n-breadcrumb-item <n-breadcrumb-item
v-for="dir in currentDirectory?.directoryArray" v-for="dir in currentDirectory?.directoryArray"
@click="setCurrentDirectory(dir)" @click="setCurrentDirectory(dir)"
:clickable="true" :clickable="true">
>
<div>{{ dir.name }}</div> <div>{{ dir.name }}</div>
</n-breadcrumb-item> </n-breadcrumb-item>
</n-breadcrumb> </n-breadcrumb>
@@ -26,21 +30,33 @@
<!-- </n-scrollbar> --> <!-- </n-scrollbar> -->
</div> </div>
<div class="container-top-tools"> <div class="container-top-tools">
<n-icon class="container-top-tools-item" size="15" @click="handleBack"> <n-icon
class="container-top-tools-item"
size="15"
@click="handleBack">
<ChevronBackOutline /> <ChevronBackOutline />
</n-icon> </n-icon>
<n-icon class="container-top-tools-item" size="14" @click="handleReload"> <n-icon
<ReloadOutline class="container-top-tools-item-reload" /> class="container-top-tools-item"
size="14"
@click="handleReload">
<ReloadOutline
class="container-top-tools-item-reload" />
</n-icon> </n-icon>
<n-icon class="container-top-tools-item" size="20" @click="emit('exit')"> <n-icon
class="container-top-tools-item"
size="20"
@click="emit('exit')">
<CloseOutline /> <CloseOutline />
</n-icon> </n-icon>
</div> </div>
</div> </div>
<div <div
class="container-items" class="container-items"
:style="{ backgroundColor: index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)' }" :style="{
> backgroundColor:
index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)'
}">
<n-scrollbar> <n-scrollbar>
<transition-group name="dirItem"> <transition-group name="dirItem">
<miaoDirectoryItem <miaoDirectoryItem
@@ -50,7 +66,7 @@
:key="dir.uid" :key="dir.uid"
:color="props.color" :color="props.color"
@delete="handleItemDelete(dir)" @delete="handleItemDelete(dir)"
/> @drag-start="handleItemDragStart" />
</transition-group> </transition-group>
<transition-group name="dirItem"> <transition-group name="dirItem">
<miaoDirectoryItem <miaoDirectoryItem
@@ -59,182 +75,196 @@
:key="file.uid" :key="file.uid"
@click="openUrl(`${baseUrl}${api.get}${file.path}`)" @click="openUrl(`${baseUrl}${api.get}${file.path}`)"
@download=" @download="
openUrl(`${baseUrl}${api.get}${file.path}`, { download: file.name }) openUrl(`${baseUrl}${api.get}${file.path}`, {
download: file.name
})
" "
@delete="handleItemDelete(file)" @delete="handleItemDelete(file)"
:color="props.color" @drag-start="handleItemDragStart"
/> :color="props.color" />
</transition-group> </transition-group>
</n-scrollbar> </n-scrollbar>
</div> </div>
<div <div
class="container-bottom" class="container-bottom"
:style="{ backgroundColor: index % 2 === 1 ? 'rgb(61 61 61)' : 'rgb(162 162 162)' }" :style="{
></div> backgroundColor:
index % 2 === 1 ? 'rgb(61 61 61)' : 'rgb(162 162 162)'
}"></div>
<miao-mask v-model:show="showModel"></miao-mask> <miao-mask v-model:show="showModel"></miao-mask>
<!-- <miao-popup-input ref="popupInput"></miao-popup-input> --> <!-- <miao-popup-input ref="popupInput"></miao-popup-input> -->
</div> </div>
</miao-drop-handler>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { NBreadcrumbItem, NBreadcrumb, NIcon, NScrollbar } from "naive-ui"; import { NBreadcrumbItem, NBreadcrumb, NIcon, NScrollbar } from 'naive-ui'
import type { file, directory } from "@/types/type.ts"; import type { file, directory } from '@/types/type.ts'
import VirtualDirectory, { VirtualFile } from "@/class/VirtualDirectory"; import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from 'vue'
import Config from "@/config"; import Config from '@/config'
import dropHandler, { dropHandlerHooks } from "@/hooks/dropHandler"; import miaoMask from '@/components/miaoMask.vue'
import miaoMask from "@/components/miaoMask.vue"; import miaoDirectoryItem from '@/components/miaoDirectory/miaoDirectoryItem.vue'
import miaoPopupInput from "@/components/miaoPopupInput.vue"; // import miaoPopupInput from "@/components/miaoPopupInput.vue";
import miaoDirectoryItem from "@/components/miaoDirectory/miaoDirectoryItem.vue"; import miaoDropHandler from '@/components/miaoDropHandler.vue'
import useMiaoFetchApi from "@/hooks/useMiaoFetchApi"; import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
import useDataBus from "@/hooks/useDataBus"; import useDataBus from '@/hooks/useDataBus'
import { import {
CloudOutline, CloudOutline,
ChevronBackOutline, ChevronBackOutline,
ReloadOutline, ReloadOutline,
CloseOutline, CloseOutline
} from "@vicons/ionicons5"; } from '@vicons/ionicons5'
import useUploadQueue from "@/hooks/useUploadQueue"; import useUploadQueue from '@/hooks/useUploadQueue'
import useViretualPages from "@/hooks/useVirtualPages"; import useVirtualPages from '@/hooks/useVirtualPages'
const { baseUrl, api } = Config; const { baseUrl, api } = Config
const dataBus = useDataBus(); const dataBus = useDataBus()
const views = useViretualPages(); const views = useVirtualPages()
const miaoFetchApi = useMiaoFetchApi(); const miaoFetchApi = useMiaoFetchApi()
const props = defineProps<{ const props = defineProps<{
color: string; color: string
id: number; id: number
}>(); }>()
const emit = defineEmits<{ const emit = defineEmits<{
exit: []; exit: []
}>(); }>()
const popupInput = ref() const popupInput = ref()
// 是否显示模态框 // 是否显示模态框
const showModel = ref(0); const showModel = ref(0)
const currentObjects = defineModel<VirtualDirectory[]>("currentObjects", { const currentObjects = defineModel<VirtualDirectory[]>('currentObjects', {
required: true, required: true
}); })
// 目前展示的Dir // 目前展示的Dir
const currentDirectory = computed(() => { const currentDirectory = computed(() => {
return currentObjects.value[0]; return currentObjects.value[0]
}); })
const index = computed(() => { const index = computed(() => {
for (let i in views) { for (let i in views) {
if (views[i].uid === props.id) { if (views[i].uid === props.id) {
return parseInt(i); return parseInt(i)
} }
} }
return 0; return 0
}); })
// 用于展示的数据,后续会增加排序等操作 // 用于展示的数据,后续会增加排序等操作
const showData_directory = computed<VirtualDirectory[]>(() => { const showData_directory = computed<VirtualDirectory[]>(() => {
const data = currentDirectory.value?.directorys const data = currentDirectory.value?.directorys
? [...currentDirectory.value?.directorys] ? [...currentDirectory.value?.directorys]
: []; : []
return data; return data
}); })
const showData_files = computed<VirtualFile[]>(() => { const showData_files = computed<VirtualFile[]>(() => {
const data = currentDirectory.value?.files ? [...currentDirectory.value?.files] : []; const data = currentDirectory.value?.files
return data; ? [...currentDirectory.value?.files]
}); : []
return data
})
// 用于监听拖拽文件事件 // 用于监听拖拽文件事件
const rootDomRef = ref<HTMLDivElement>(); const rootDomRef = ref<HTMLDivElement>()
const uploadQueue = useUploadQueue(); const uploadQueue = useUploadQueue()
// 设置显示的dir // 设置显示的dir
const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => { const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => {
// 若没有缓存数据则同步刷新,有缓存数据则异步刷新 // 若没有缓存数据则同步刷新,有缓存数据则异步刷新
if (virtualDirectory.files === undefined || virtualDirectory.directorys === undefined) { if (
const data: (file | directory)[] = await miaoFetchApi.get(virtualDirectory); virtualDirectory.files === undefined ||
virtualDirectory.updateContent(data); virtualDirectory.directorys === undefined
) {
const data: (file | directory)[] = await miaoFetchApi.get(
virtualDirectory
)
virtualDirectory.updateContent(data)
} else { } else {
(async () => { ;(async () => {
const data: (file | directory)[] = await miaoFetchApi.get(virtualDirectory); const data: (file | directory)[] = await miaoFetchApi.get(
virtualDirectory.updateContent(data); virtualDirectory
})(); )
virtualDirectory.updateContent(data)
})()
} }
currentObjects.value[0] = virtualDirectory; currentObjects.value[0] = virtualDirectory
}; }
const reload = () => { const reload = () => {
setCurrentDirectory(currentDirectory.value); setCurrentDirectory(currentDirectory.value)
}; }
const openUrl = ( const openUrl = (
href: string, href: string,
config?: { config?: {
download?: string; download?: string
target?: string; target?: string
} }
) => { ) => {
let { target, download } = config ?? {}; let { target, download } = config ?? {}
const a = document.createElement("a"); const a = document.createElement('a')
a.setAttribute("href", href); a.setAttribute('href', href)
a.setAttribute("target", target ?? "_blank"); a.setAttribute('target', target ?? '_blank')
download && a.setAttribute("download", download); download && a.setAttribute('download', download)
a.click(); a.click()
}; }
const handleBack = () => { const handleBack = () => {
if (currentDirectory.value?.parent) { if (currentDirectory.value?.parent) {
setCurrentDirectory(currentDirectory.value.parent); setCurrentDirectory(currentDirectory.value.parent)
} }
}; }
const handleReload = () => { const handleReload = () => {
reload(); reload()
}; }
const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => { const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => {
const { response } = miaoFetchApi.delete(item, { const { response } = miaoFetchApi.delete(item, {
retry: 5, retry: 5
}); })
// todo: 将删除事件统一用一个事件管理中心管理 // todo: 将删除事件统一用一个事件管理中心管理
const id = (await response).eventId; const id = (await response).eventId
const { response: res } = miaoFetchApi.query(id); const { response: res } = miaoFetchApi.query(id)
const eventResult = await res; const eventResult = await res
if (eventResult.status === "success") { if (eventResult.status === 'success') {
reload(); reload()
} }
}; }
onMounted(async () => { const handleItemDragStart = async (
await setCurrentDirectory(currentDirectory.value); _event: DragEvent,
// 拖动事件处理 item: VirtualDirectory | VirtualFile
const dropFileOption: dropHandlerHooks = { ) => {
onDropFiles(files: File[]) { if (item.type === 'file') {
const dragData = dataBus.pop("dragData"); dataBus.set('dragData_vFiles', [item])
console.log({ dragData, files }); } else {
if (files && files.length > 0) { dataBus.set('dragData_vDirectory', [item])
}
}
const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
setCurrentDirectory(vDirs[0])
}
const handleDropFiles = async (files: File[]) => {
currentDirectory.value && currentDirectory.value &&
uploadQueue.push(files, currentDirectory.value, { uploadQueue.push(files, currentDirectory.value, {
onFinish() { onFinish() {
reload(); reload()
},
});
} }
}, })
onEnter() { }
showModel.value += 1;
},
onEnd() {
showModel.value -= 1;
},
};
rootDomRef.value && dropHandler(rootDomRef.value, dropFileOption);
console.log(await popupInput.value.popup()) onMounted(async () => {
}); await setCurrentDirectory(currentDirectory.value)
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -260,7 +290,7 @@ $bottom-bar-height: 60px;
overflow: hidden; overflow: hidden;
background-color: #f1f1f1; background-color: #f1f1f1;
.container-top-colorbar { .container-top-colorfulBar {
height: 70%; height: 70%;
width: 5px; width: 5px;
margin-left: 10px; margin-left: 10px;
@@ -356,23 +386,23 @@ $bottom-bar-height: 60px;
} }
// transition动画相关 // transition动画相关
.miao-directory-item-container { // .miao-directory-item-container {
.container-items { // .container-items {
.dirItem-move { // .dirItem-move {
transition: all 0.2s ease; // transition: all 0.2s ease;
} // }
.dirItem-leave-active { // .dirItem-leave-active {
transition: none; // transition: none;
} // }
.dirItem-enter-from { // .dirItem-enter-from {
opacity: 0; // opacity: 0;
} // }
.dirItem-leave-active { // .dirItem-leave-active {
position: absolute; // position: absolute;
} // }
} // }
} // }
</style> </style>