Compare commits
3
Commits
8b15cd9403
...
1.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5719012c2c | ||
|
|
1b5abdf40c | ||
|
|
3d22ed0c71 |
No files matched your search
@@ -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的多选功能
|
||||
@@ -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"
|
||||
|
||||
Generated
+3
@@ -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,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();
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+89
-83
@@ -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[]) => {
|
||||
@@ -350,7 +350,6 @@ const handleDropVDirectory = async (vDirs: VirtualDirectory[]) => {
|
||||
}
|
||||
})()
|
||||
}
|
||||
// setCurrentDirectory(vDirs[0])
|
||||
}
|
||||
|
||||
const handleDropVirtualFiles = (files: VirtualFile[]) => {
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user