3 Commits
8 changed files with 168 additions and 95 deletions

No files matched your search

+1 -1
View File
@@ -6,6 +6,6 @@ todo: miaoDirectory的重命名功能 [finish]
todo: miaoDirectory的剪切功能 [finish]
todo: miaoDirectory的上传按钮 [finish]
todo: miaoDirectory的新建文件/文件夹功能 [finish]
todo: miaoDirectory的多选功能 [finish]
todo: VirtualDirectory更新时更新文件信息
todo: miaoDirectory的复制文件/文件夹功能
todo: miaoDirectory的多选功能
+1
View File
@@ -12,6 +12,7 @@
"dependencies": {
"@vicons/ionicons5": "^0.12.0",
"axios": "^1.7.3",
"lodash": "^4.17.21",
"naive-ui": "^2.39.0",
"sass": "^1.77.8",
"vue": "^3.4.35"
+3
View File
@@ -14,6 +14,9 @@ importers:
axios:
specifier: ^1.7.3
version: 1.7.3
lodash:
specifier: ^4.17.21
version: 4.17.21
naive-ui:
specifier: ^2.39.0
version: 2.39.0(vue@3.4.37(typescript@5.5.4))
+1
View File
@@ -1,5 +1,6 @@
import generateId from "@/hooks/generateId";
import useMiaoFetchApi from "@/hooks/useMiaoFetchApi";
// import { debounce } from "lodash";
import type { stats, file, directory } from "@/types/type.ts";
const miaoFetchApi = useMiaoFetchApi();
+2 -1
View File
@@ -37,7 +37,7 @@ const onClickoutside = async () => {
const handleMainMouseLeave = async () => {
setTimeout(() => {
showContextMenu.value -= 1
}, 100)
}, 20)
// nextTick(()=>{
// showContextMenu.value -= 1
// })
@@ -45,6 +45,7 @@ const handleMainMouseLeave = async () => {
const handleSelect = async (key: string) => {
emit('select', key)
showContextMenu.value = 0
}
const handleContextMenu = (e: MouseEvent) => {
@@ -1,12 +1,13 @@
<template>
<div class="miao-item" draggable="true" ref="itemRef" @dragstart="handleDragStart">
<miaoContextMenu :options="dropDownOptions" :touch-time-out="500" @select="handleDropdownSelect">
<div class="item-main">
<div class="item-main" :class="props.selected ? 'item-main-selected' : ''" ref="mainRef">
<div class="item-main-front" :style="{ backgroundColor: props.color }"></div>
<Icon class="item-main-icon"></Icon>
<Icon class="item-main-icon" v-show="clientWidth > 250"></Icon>
<div class="item-main-info">
<div class="item-main-info-name">
{{ name }}
<Icon class="item-main-info-name-icon" v-show="clientWidth <= 250"></Icon>
<n-ellipsis style="max-width: 140px;">{{ name }}</n-ellipsis>
</div>
<div class="item-main-info-date" v-if="time">
{{ time }}
@@ -38,16 +39,18 @@ import {
PlayCircle,
Disc
} from '@vicons/ionicons5'
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import dateFormatter from '@/hooks/dateFormatter'
import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
import { NEllipsis } from 'naive-ui'
// import useMiaoFetchApi from '@/hooks/useMiaoFetchApi'
const miaoFetchApi = useMiaoFetchApi()
// const miaoFetchApi = useMiaoFetchApi()
const props = defineProps<{
item: VirtualDirectory | VirtualFile
name?: string
color: string
selected: boolean
}>()
const emit = defineEmits<{
@@ -55,10 +58,16 @@ const emit = defineEmits<{
delete: []
rename: []
dragStart: [event: DragEvent, item: VirtualDirectory | VirtualFile]
onSelected: []
}>()
const itemType = ref<'file' | 'directory'>(props.item.type)
const mainRef = ref<HTMLDivElement>()
// 使用ResizeObserver实现
const clientWidth = ref<number>(666)
const name = props.name ?? props.item.name
const time = computed(() => {
@@ -89,6 +98,25 @@ const dropDownOptions = computed(() => {
]
)
}
if (props.selected === false) {
options.push(
...[
{
label: '多选:选择',
key: 'select'
}
]
)
} else {
options.push(
...[
{
label: '多选:取消',
key: 'select'
}
]
)
}
return options
})
@@ -100,6 +128,8 @@ const handleDropdownSelect = (key: string) => {
emit('download')
} else if (key === 'delete') {
emit('delete')
} else if (key === 'select') {
emit('onSelected')
}
}
@@ -164,13 +194,24 @@ const handleDragStart = (event: DragEvent) => {
emit('dragStart', event, props.item)
// dataBus.set('dragData', [props.item])
}
onMounted(() => {
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
clientWidth.value = entry.contentRect.width;
}
});
if (mainRef.value) {
observer.observe(mainRef.value);
}
})
</script>
<style lang="scss" scoped>
.miao-item {
cursor: pointer;
width: calc(100% - 20px);
min-width: 250px;
min-width: 170px;
// aspect-ratio: 66/9;
height: 50px;
position: relative;
@@ -187,7 +228,7 @@ const handleDragStart = (event: DragEvent) => {
border-radius: 7px;
display: flex;
align-items: center;
transition: background-color 0.3s;
transition: background-color 0.3s ease;
.item-main-front {
height: 100%;
@@ -200,6 +241,7 @@ const handleDragStart = (event: DragEvent) => {
margin-left: 10px;
height: 80%;
color: #00403e;
transition: color 0.15s ease-out;
}
.item-main-info {
@@ -209,7 +251,18 @@ const handleDragStart = (event: DragEvent) => {
flex-direction: column;
&-name {
position: relative;
user-select: none;
display: flex;
align-items: center;
margin-top: 3px;
&-icon {
color: #00403e;
width: 20px;
margin-right: 5px;
transition: color 0.15s ease-out;
}
}
&-date {
@@ -252,5 +305,11 @@ const handleDragStart = (event: DragEvent) => {
background-color: #d2d2d2;
}
}
.item-main-selected {
background-color: rgba(0, 128, 207, 0.32);
&:hover {
background-color: rgba(0, 128, 207, 0.64);;
}
}
}
</style>
+2
View File
@@ -70,8 +70,10 @@ defineExpose({ popup });
width: 100%;
display: flex;
justify-content: flex-end;
flex-wrap: wrap;
.popup-button-container-button {
margin-top: 3px;
&:not(:last-child) {
margin-right: 5px;
}
+91 -85
View File
@@ -1,18 +1,12 @@
<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="{
<div class="container-top" :style="{
backgroundColor:
index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)'
}">
<div
class="container-top-colorfulBar"
:style="{ backgroundColor: props.color }"></div>
<div class="container-top-colorfulBar" :style="{ backgroundColor: props.color }"></div>
<div class="container-top-breadcrumb">
<!-- <n-scrollbar x-scrollable> -->
<div class="container-top-breadcrumb-container">
@@ -22,10 +16,8 @@
<CloudOutline />
</n-icon>
</n-breadcrumb-item>
<n-breadcrumb-item
v-for="dir in currentDirectory?.getParents"
@click="setCurrentDirectory(dir)"
:clickable="true">
<n-breadcrumb-item v-for="dir in currentDirectory?.getParents"
@click="setCurrentDirectory(dir)" :clickable="true">
<div>{{ dir.name }}</div>
</n-breadcrumb-item>
</n-breadcrumb>
@@ -33,87 +25,60 @@
<!-- </n-scrollbar> -->
</div>
<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 />
</n-icon>
<n-icon
class="container-top-tools-item"
size="14"
@click="handleReload">
<ReloadOutline
class="container-top-tools-item-reload" />
<n-icon class="container-top-tools-item" size="14" @click="handleReload">
<ReloadOutline class="container-top-tools-item-reload" />
</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 />
</n-icon>
</div>
</div>
<div
class="container-items"
:style="{
<div class="container-items" :style="{
backgroundColor:
index % 2 === 0 ? '#f8f8f8' : 'rgb(240 240 240)'
}">
<n-scrollbar>
<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"
@rename="handleItemRename(dir)" />
<miaoDirectoryItem v-for="dir in showData_directory" :item="dir" :key="dir.uid"
:color="props.color" :selected="selectedItem.value.includes(dir)"
@delete="handleItemDelete(dir)" @click="handleItemClick(dir)"
@drag-start="handleItemDragStart" @rename="handleItemRename(dir)"
@on-selected="handleItemSelect(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="handleItemDownload(file)"
@delete="handleItemDelete(file)"
@drag-start="handleItemDragStart"
@rename="handleItemRename(file)"
:color="props.color" />
<miaoDirectoryItem v-for="file in showData_files" :item="file" :key="file.uid"
:color="props.color" :selected="selectedItem.value.includes(file)"
@click="handleItemClick(file)" @download="handleItemDownload(file)"
@delete="handleItemDelete(file)" @drag-start="handleItemDragStart"
@rename="handleItemRename(file)" @on-selected="handleItemSelect(file)" />
</transition-group>
</n-scrollbar>
</div>
<div
class="container-bottom"
:style="{
<div class="container-bottom" :style="{
backgroundColor:
index % 2 === 1 ? 'rgb(61 61 61)' : 'rgb(162 162 162)'
}">
<div class="container-bottom-item">
<div class="container-bottom-item-icon">
<add-outline
:style="{
<add-outline :style="{
color:
index % 2 === 0
? 'rgb(0 0 0)'
: 'rgb(255 255 255)'
}"
@click="handleAddNewItem" />
}" @click="handleAddNewItem" />
</div>
</div>
<div class="container-bottom-item">
<div class="container-bottom-item-icon">
<cloud-upload-outline
:style="{
<cloud-upload-outline :style="{
color:
index % 2 === 0
? 'rgb(0 0 0)'
: 'rgb(255 255 255)'
}"
@click="handlePickFilesUpload" />
}" @click="handlePickFilesUpload" />
</div>
</div>
</div>
@@ -125,9 +90,8 @@
<script setup lang="ts">
import { NBreadcrumbItem, NBreadcrumb, NIcon, NScrollbar } from 'naive-ui'
import type { file, directory } from '@/types/type.ts'
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory'
import { computed, onMounted, ref } from 'vue'
import { computed, onMounted, reactive, ref } from 'vue'
import Config from '@/config'
import miaoMask from '@/components/miaoMask.vue'
import miaoDirectoryItem from '@/components/miaoDirectory/miaoDirectoryItem.vue'
@@ -146,6 +110,7 @@ import {
import useUploadQueue from '@/hooks/useUploadQueue'
import useVirtualPages from '@/hooks/useVirtualPages'
import { filePicker } from '@/hooks/miaoTools'
import { uniq } from 'lodash'
const { baseUrl, api } = Config
const dataBus = useDataBus()
@@ -166,6 +131,34 @@ const popupInput = ref()
// 是否显示模态框
const showModel = ref(0)
class SelectedItem {
constructor() {
this._selectedItem = []
}
_selectedItem: (VirtualDirectory | VirtualFile)[]
push(item: VirtualDirectory | VirtualFile) {
this._selectedItem = [item, ...this._selectedItem]
}
remove(item: VirtualDirectory | VirtualFile) {
this._selectedItem = this._selectedItem.filter(v => v !== item)
}
clear() {
this._selectedItem = []
}
// 保证内容仍然存在于dir中且各个元素不重复
get value() {
return uniq(this._selectedItem).filter(v => {
if (currentDirectory.value === undefined) {
return false
}
// @ts-ignore
return [...currentDirectory.value?.directorys, ...currentDirectory.value?.files].includes(v)
})
}
}
// 已选择的item,用于多选功能
const selectedItem = reactive(new SelectedItem())
const currentObjects = defineModel<VirtualDirectory[]>('currentObjects', {
required: true
})
@@ -211,17 +204,9 @@ const setCurrentDirectory = async (virtualDirectory: VirtualDirectory) => {
virtualDirectory.files === undefined ||
virtualDirectory.directorys === undefined
) {
const data: (file | directory)[] = await miaoFetchApi.get(
virtualDirectory
)
virtualDirectory.updateContent(data)
await virtualDirectory.update()
} else {
;(async () => {
const data: (file | directory)[] = await miaoFetchApi.get(
virtualDirectory
)
virtualDirectory.updateContent(data)
})()
virtualDirectory.update()
}
currentObjects.value[0] = virtualDirectory
}
@@ -255,13 +240,27 @@ const handleReload = () => {
reload()
}
const handleItemClick = (item: VirtualDirectory | VirtualFile) => {
if (selectedItem.value.length > 0) {
handleItemSelect(item)
} else {
if (item.type === 'file') {
openUrl(`${baseUrl}${api.get}${item.path}`)
} else if (item.type === 'directory') {
setCurrentDirectory(item)
}
}
}
const handleItemDownload = (item: VirtualFile) => {
openUrl(`${baseUrl}${api.get}${item.path}`, {
download: item.name
})
}
const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => {
const handleItemDelete = async (_item: VirtualDirectory | VirtualFile) => {
for (let item of uniq([...selectedItem.value, _item])) {
; (async () => {
const { response } = miaoFetchApi.delete(item, {
retry: 5
})
@@ -272,6 +271,8 @@ const handleItemDelete = async (item: VirtualDirectory | VirtualFile) => {
if (eventResult.status === 'success') {
reload()
}
})()
}
}
const handleItemRename = async (item: VirtualDirectory | VirtualFile) => {
@@ -314,11 +315,10 @@ const handleItemDragStart = async (
_event: DragEvent,
item: VirtualDirectory | VirtualFile
) => {
if (item.type === 'file') {
dataBus.set('dragData_vFiles', [item])
} else {
dataBus.set('dragData_vDirectory', [item])
}
const items = uniq([item, ...selectedItem.value])
dataBus.set('dragData_vFiles', items.filter(v => v.type === 'file'))
dataBus.set('dragData_vDirectory', items.filter(v => v.type === 'directory'))
selectedItem.clear()
}
const handleDropFiles = async (files: File[]) => {
@@ -338,7 +338,7 @@ const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
}
}
for (let dir of vDirs) {
;(async () => {
; (async () => {
const _from = dir.parent
const { response } = miaoFetchApi.cut(dir, currentDirectory.value)
const id = (await response).eventId
@@ -350,7 +350,6 @@ const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
}
})()
}
// setCurrentDirectory(vDirs[0])
}
const handleDropVirtualFiles = (files: VirtualFile[]) => {
@@ -358,7 +357,7 @@ const handleDropVirtualFiles = (files: VirtualFile[]) => {
return
}
for (let file of files) {
;(async () => {
; (async () => {
const _from = file.parent
const { response } = miaoFetchApi.cut(file, currentDirectory.value)
const id = (await response).eventId
@@ -413,7 +412,6 @@ const handleAddNewItem = async () => {
if (popupRes.key === 'cancel' || popupRes.text === '') {
return
}
console.log(popupRes)
const _currDir = currentDirectory.value
const name = popupRes.text
const opType: 'file' | 'dir' = popupRes.key
@@ -430,6 +428,14 @@ const handleAddNewItem = async () => {
}
}
}
const handleItemSelect = (item: VirtualDirectory | VirtualFile) => {
if (!selectedItem.value.includes(item)) {
selectedItem.push(item)
} else {
selectedItem.remove(item)
}
}
onMounted(async () => {
await setCurrentDirectory(currentDirectory.value)
})
@@ -443,7 +449,7 @@ $bottom-bar-height: 60px;
position: relative;
height: 100%;
width: inherit;
min-width: 266px;
// min-width: 236px;
display: flex;
flex-direction: column;
overflow: hidden;
@@ -557,6 +563,7 @@ $bottom-bar-height: 60px;
display: flex;
justify-content: center;
align-items: center;
.container-bottom-item-icon {
svg {
width: 30px;
@@ -595,5 +602,4 @@ $bottom-bar-height: 60px;
// position: absolute;
// }
// }
// }
</style>
// }</style>