refactor:重构路径输入组件

This commit is contained in:
jxxghp
2025-01-17 13:28:31 +08:00
parent 1fe8aeb9e1
commit 822711a530
4 changed files with 147 additions and 30 deletions

View File

@@ -1,6 +1,5 @@
<script lang="ts" setup>
import type { TransferDirectoryConf } from '@/api/types'
import PathInput from '@/components/input/PathInput.vue'
import api from '@/api'
import { nextTick } from 'vue'
import { storageOptions } from '@/api/constants'
@@ -20,12 +19,6 @@ const props = defineProps({
height: String,
})
// 下载路径
const downloadPath = ref<string>('')
// 媒体库路径
const libraryPath = ref<string>('')
// 卡版是否折叠状态
const isCollapsed = ref(true)
@@ -210,16 +203,12 @@ watch(
/>
</VCol>
<VCol cols="8">
<PathInput v-model="props.directory.download_path" :storage="props.directory.storage">
<template #activator="{ menuprops }">
<VTextField
:model-value="props.directory.download_path"
v-bind="menuprops"
variant="underlined"
label="下载目录/源目录"
/>
</template>
</PathInput>
<VPathField
v-model="props.directory.download_path"
:storage="props.directory.storage"
variant="underlined"
label="下载目录/源目录"
/>
</VCol>
<VCol cols="6" v-if="!props.directory.media_type || props.directory.media_type === ''">
<VSwitch v-model="props.directory.download_type_folder" label="按类型分类"></VSwitch>
@@ -257,16 +246,12 @@ watch(
/>
</VCol>
<VCol cols="8">
<PathInput v-model="props.directory.library_path" :storage="props.directory.library_storage">
<template #activator="{ menuprops }">
<VTextField
:modelValue="props.directory.library_path"
v-bind="menuprops"
variant="underlined"
label="媒体库目录"
/>
</template>
</PathInput>
<VPathField
v-model="props.directory.library_path"
:storage="props.directory.library_storage"
variant="underlined"
label="媒体库目录"
/>
</VCol>
<VCol cols="4">
<VSelect

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import PathInput from '@/components/input/PathInput.vue'
const attrs = useAttrs()
const props = defineProps({
modelValue: {
type: String,
default: '* * * * *',
},
storage: {
type: String,
default: 'local',
},
})
const emit = defineEmits(['update:modelValue'])
const innerValue = ref(props.modelValue)
watch(
() => props.modelValue,
value => {
innerValue.value = value
},
)
const propsWithoutModelValue = computed(() => {
const { modelValue, ...rest } = props
return { ...rest, ...attrs }
})
function updateModelValue(value: string) {
innerValue.value = value
emit('update:modelValue', value)
}
</script>
<template>
<PathInput v-model="innerValue" :storage="props.storage" @update:modelValue="updateModelValue">
<template #activator="{ menuprops }">
<VTextField
:modelValue="innerValue"
@update:modelValue="updateModelValue"
v-bind="{ ...menuprops, ...propsWithoutModelValue }"
/>
</template>
</PathInput>
</template>

View File

@@ -3,6 +3,10 @@ import api from '@/api'
import { FileItem } from '@/api/types'
const props = defineProps({
modelValue: {
type: String,
default: '/',
},
root: {
type: String,
default: '/',
@@ -16,7 +20,7 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue'])
const activedDirs = ref<Record<string, any>[]>([])
const menuVisible = ref(false)
const treeItems = ref<FileItem[]>([
{
@@ -29,16 +33,70 @@ const treeItems = ref<FileItem[]>([
},
])
const activedDirs = ref<FileItem[]>([])
const openedDirs = ref<FileItem[]>([])
// 调用API查询子目录
async function fetchDirs(item: any) {
return api
.post('/storage/list', item)
.then((data: any) => {
data = data.filter((i: any) => i.type === 'dir')
item.children.push(...data)
item.children?.push(...data)
})
.catch(err => console.warn(err))
}
// 递归查询路径
function findPath(item: FileItem, path: string): FileItem | null {
if (item.path === path) {
return item
}
if (item.children) {
for (const child of item.children) {
const res: FileItem | null = findPath(child, path)
if (res) {
return res
}
}
}
return null
}
// 根据路径展开所有子目录
async function expandDirs(path: string) {
// 分割路径
const paths = path.split('/').filter(i => i)
// 展开根目录
const root_item = treeItems.value[0]
await fetchDirs(root_item)
openedDirs.value.push(root_item)
// 逐级展开
let currentPath = '/'
for (const p of paths) {
currentPath += `${p}/`
// 查询当前目录
const item = findPath(root_item, currentPath)
if (!item) {
break
}
// 加载子目录
if (item.children?.length === 0) {
await fetchDirs(item)
}
// 打开当前目录
if (!openedDirs.value.includes(item) && path != currentPath) {
openedDirs.value.push(item)
}
// 选中当前目录
if (path == currentPath) {
activedDirs.value = [item]
}
}
}
// 当前选中项
const selectedPath = computed(() => {
if (activedDirs.value.length > 0) {
return activedDirs.value[0].path
@@ -51,6 +109,27 @@ watch(activedDirs, newVal => {
emit('update:modelValue', selectedPath.value)
})
watch(
() => menuVisible.value,
async visible => {
if (visible) {
treeItems.value = [
{
name: '/',
path: props.root,
children: [],
type: 'dir',
basename: props.root,
storage: props.storage,
},
]
openedDirs.value = []
activedDirs.value = []
await expandDirs(props.modelValue)
}
},
)
watch(
() => props.storage,
async newVal => {
@@ -65,18 +144,20 @@ watch(
},
]
activedDirs.value = []
openedDirs.value = []
},
)
</script>
<template>
<div>
<VMenu :close-on-content-click="false" content-class="cursor-default">
<VMenu v-model="menuVisible" :close-on-content-click="false" content-class="cursor-default">
<template v-slot:activator="{ props }">
<slot name="activator" :menuprops="props" />
</template>
<VTreeview
v-model:activated="activedDirs"
v-model:opened="openedDirs"
:items="treeItems"
:load-children="fetchDirs"
item-key="path"