fix:优化文件管理

This commit is contained in:
jxxghp
2024-06-20 11:39:25 +08:00
parent e0ff98b1d7
commit 6a5958409a
7 changed files with 149 additions and 140 deletions
+8 -10
View File
@@ -11,10 +11,6 @@ import { isNullOrEmptyObject } from '@/@core/utils'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
storages: String, storages: String,
path: String,
fileid: String,
pickcode: String,
fileidstack: Array as PropType<string[]>,
tree: Boolean, tree: Boolean,
endpoints: Object as PropType<EndPoints>, endpoints: Object as PropType<EndPoints>,
axios: { axios: {
@@ -22,6 +18,11 @@ const props = defineProps({
required: true, required: true,
}, },
axiosconfig: Object, axiosconfig: Object,
item: {
type: Object as PropType<FileItem>,
required: true,
},
fileidstack: Array as PropType<string[]>,
}) })
// 对外事件 // 对外事件
@@ -165,10 +166,9 @@ function u115AuthDone() {
<template> <template>
<VCard class="mx-auto" :loading="loading > 0"> <VCard class="mx-auto" :loading="loading > 0">
<div v-if="activeStorage && (path || fileid)"> <div v-if="activeStorage && item">
<FileToolbar <FileToolbar
:path="path" :item="item"
:fileid="fileid"
:fileidstack="fileidstack" :fileidstack="fileidstack"
:storages="storagesArray" :storages="storagesArray"
:storage="activeStorage" :storage="activeStorage"
@@ -180,9 +180,7 @@ function u115AuthDone() {
@sortchanged="sortChanged" @sortchanged="sortChanged"
/> />
<FileList <FileList
:path="path" :item="item"
:fileid="fileid"
:pickcode="pickcode"
:storage="activeStorage" :storage="activeStorage"
:icons="fileIcons" :icons="fileIcons"
:endpoints="endpoints" :endpoints="endpoints"
+74 -85
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Axios } from 'axios' import type { Axios, AxiosRequestConfig } from 'axios'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { useConfirm } from 'vuetify-use-dialog' import { useConfirm } from 'vuetify-use-dialog'
import { useToast } from 'vue-toast-notification' import { useToast } from 'vue-toast-notification'
@@ -24,21 +24,25 @@ const appMode = computed(() => {
const inProps = defineProps({ const inProps = defineProps({
icons: Object, icons: Object,
storage: String, storage: String,
path: String,
fileid: String,
pickcode: String,
endpoints: Object as PropType<EndPoints>, endpoints: Object as PropType<EndPoints>,
axios: { axios: {
type: Object as PropType<Axios>, type: Object as PropType<Axios>,
required: true, required: true,
}, },
refreshpending: Boolean, refreshpending: Boolean,
item: {
type: Object as PropType<FileItem>,
required: true,
},
sort: String, sort: String,
}) })
// 对外事件 // 对外事件
const emit = defineEmits(['loading', 'pathchanged', 'refreshed', 'filedeleted', 'renamed']) const emit = defineEmits(['loading', 'pathchanged', 'refreshed', 'filedeleted', 'renamed'])
// 确认框
const createConfirm = useConfirm()
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -57,9 +61,6 @@ const progressText = ref('请稍候 ...')
// 识别进度 // 识别进度
const progressValue = ref(0) const progressValue = ref(0)
// 确认框
const createConfirm = useConfirm()
// 内容列表 // 内容列表
const items = ref<FileItem[]>([]) const items = ref<FileItem[]>([])
@@ -78,7 +79,7 @@ const newName = ref('')
// 处理目录内所有文件 // 处理目录内所有文件
const renameAll = ref(false) const renameAll = ref(false)
// 当前名称 // 当前操作项
const currentItem = ref<FileItem>() const currentItem = ref<FileItem>()
// 识别结果 // 识别结果
@@ -98,35 +99,34 @@ const dirs = computed(() => items.value.filter(item => item.type === 'dir' && it
// 文件过滤 // 文件过滤
const files = computed(() => items.value.filter(item => item.type === 'file' && item.name.includes(filter.value))) const files = computed(() => items.value.filter(item => item.type === 'file' && item.name.includes(filter.value)))
// 是否目录 // 是否目录
const isDir = computed(() => inProps.path?.endsWith('/')) const isDir = computed(() => inProps.item.path?.endsWith('/'))
// 是否文件 // 是否文件
const isFile = computed(() => !isDir.value) const isFile = computed(() => !isDir.value)
// 是否为图片文件 // 是否为图片文件
const isImage = computed(() => { const isImage = computed(() => {
const ext = inProps.path?.split('.').pop()?.toLowerCase() const ext = inProps.item.path?.split('.').pop()?.toLowerCase()
return ['png', 'jpg', 'jpeg', 'gif', 'bmp'].includes(ext ?? '') return ['png', 'jpg', 'jpeg', 'gif', 'bmp'].includes(ext ?? '')
}) })
// 调API加载内容 // 调API加载文件夹内的内容
async function load() { async function list_files() {
loading.value = true loading.value = true
emit('loading', true) emit('loading', true)
// 参数 // 参数
const url = inProps.endpoints?.list.url const url = inProps.endpoints?.list.url
.replace(/{storage}/g, inProps.storage) .replace(/{storage}/g, inProps.storage)
.replace(/{path}/g, encodeURIComponent(inProps.path || ''))
.replace(/{sort}/g, inProps.sort || 'name') .replace(/{sort}/g, inProps.sort || 'name')
.replace(/{fileid}/g, inProps.fileid || '')
.replace(/{filetype}/g, isDir.value ? 'dir' : 'file') const config: AxiosRequestConfig<FileItem> = {
.replace(/{pickcode}/g, inProps.pickcode || '')
const config = {
url, url,
method: inProps.endpoints?.list.method || 'get', method: inProps.endpoints?.list.method || 'get',
data: inProps.item,
} }
// 加载数据 // 加载数据
items.value = (await inProps.axios.request(config)) ?? [] items.value = (await inProps.axios.request(config)) ?? []
emit('loading', false) emit('loading', false)
@@ -142,52 +142,45 @@ async function deleteItem(item: FileItem) {
if (confirmed) { if (confirmed) {
emit('loading', true) emit('loading', true)
const url = inProps.endpoints?.delete.url
.replace(/{storage}/g, inProps.storage)
.replace(/{path}/g, encodeURIComponent(item.path))
.replace(/{fileid}/g, item.fileid || '')
const config = { const url = inProps.endpoints?.delete.url.replace(/{storage}/g, inProps.storage)
const config: AxiosRequestConfig<FileItem> = {
url, url,
method: inProps.endpoints?.delete.method || 'post', method: inProps.endpoints?.delete.method || 'post',
data: item,
} }
await inProps.axios.request(config) await inProps.axios.request(config)
emit('filedeleted') emit('filedeleted')
emit('loading', false) emit('loading', false)
// 重新加载 // 重新加载
load() list_files()
} }
} }
// 切换路径 // 切换路径
function changePath(item: FileItem) { function changePath(item: FileItem) {
item.path = inProps.path + item.name + (item.type === 'dir' ? '/' : '') item.path = inProps.item.path + item.name + (item.type === 'dir' ? '/' : '')
emit('pathchanged', item) emit('pathchanged', item)
} }
// 新窗口中下载文件 // 新窗口中下载文件
function download(item: FileItem) { async function download(item: FileItem) {
const token = store.state.auth.token const url = inProps.endpoints?.download.url.replace(/{storage}/g, inProps.storage)
const url_path = inProps.endpoints?.download.url const filterEntries = Object.entries(item).filter(([key, value]) => !['children', 'thumbnail'].includes(key) && value)
.replace(/{storage}/g, inProps.storage) const queryParams = filterEntries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&')
.replace(/{path}/g, encodeURIComponent(item.path)) window.open(
.replace(/{fileid}/g, item.fileid || '') `${import.meta.env.VITE_API_BASE_URL}${url.slice(1)}?${queryParams}&token=${store.state.auth.token}`,
.replace(/{pickcode}/g, item.pickcode || '') '_blank',
const url = `${import.meta.env.VITE_API_BASE_URL}${url_path.slice(1)}&token=${token}` )
// 下载文件
window.open(url, '_blank')
} }
// 显示图片 // 获取图片地址
function getImgLink(item: FileItem) { function getImgLink(item: FileItem) {
const token = store.state.auth.token let url = inProps.endpoints?.image.url.replace(/{storage}/g, inProps.storage)
const url_path = inProps.endpoints?.image.url const filterEntries = Object.entries(item).filter(([key, value]) => !['children', 'thumbnail'].includes(key) && value)
.replace(/{storage}/g, inProps.storage) const queryParams = filterEntries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&')
.replace(/{path}/g, encodeURIComponent(item.path)) return `${import.meta.env.VITE_API_BASE_URL}${url.slice(1)}?${queryParams}&token=${store.state.auth.token}`
.replace(/{fileid}/g, item.fileid || '')
.replace(/{pickcode}/g, item.pickcode || '')
return `${import.meta.env.VITE_API_BASE_URL}${url_path.slice(1)}&token=${token}`
} }
// 显示重命名弹窗 // 显示重命名弹窗
@@ -204,7 +197,7 @@ async function get_recommend_name() {
try { try {
const result: { [key: string]: any } = await api.get('transfer/name', { const result: { [key: string]: any } = await api.get('transfer/name', {
params: { params: {
path: `${inProps.path}${currentItem.value?.name}`, path: `${inProps.item.path}${currentItem.value?.name}`,
filetype: currentItem.value?.type ?? 'file', filetype: currentItem.value?.type ?? 'file',
}, },
}) })
@@ -223,22 +216,6 @@ async function get_recommend_name() {
async function rename() { async function rename() {
emit('loading', true) emit('loading', true)
let url = inProps.endpoints?.rename.url
.replace(/{storage}/g, inProps.storage)
.replace(/{path}/g, encodeURIComponent(currentItem.value?.path || ''))
.replace(/{fileid}/g, currentItem.value?.fileid || '')
.replace(/{newname}/g, encodeURIComponent(newName.value))
.replace(/{filetype}/g, currentItem.value?.type || 'file')
if (renameAll.value) {
url += '&recursive=true'
}
const config = {
url,
method: inProps.endpoints?.mkdir.method || 'post',
}
// 关闭弹窗 // 关闭弹窗
renamePopper.value = false renamePopper.value = false
@@ -255,6 +232,18 @@ async function rename() {
} }
// 调API // 调API
let url = inProps.endpoints?.rename.url
.replace(/{storage}/g, inProps.storage)
.replace(/{newname}/g, encodeURIComponent(newName.value))
if (renameAll.value) {
url += '&recursive=true'
}
const config: AxiosRequestConfig<FileItem> = {
url,
method: inProps.endpoints?.rename.method || 'post',
data: currentItem.value,
}
const result: { [key: string]: any } = await inProps.axios?.request(config) const result: { [key: string]: any } = await inProps.axios?.request(config)
if (!result.success) { if (!result.success) {
$toast.error(result.message) $toast.error(result.message)
@@ -284,9 +273,20 @@ function formatTime(timestape: number) {
return new Date(timestape * 1000).toLocaleString() return new Date(timestape * 1000).toLocaleString()
} }
// 监听path变化或者storage变化 // 监听refreshPending变化
watch( watch(
[() => inProps.path, () => inProps.fileid, () => inProps.storage], () => inProps.refreshpending,
async () => {
if (inProps.refreshpending) {
await list_files()
emit('refreshed')
}
},
)
// 监听item变化或者storage变化
watch(
[() => inProps.item, () => inProps.storage],
async () => { async () => {
// 清空列表 // 清空列表
items.value = [] items.value = []
@@ -346,22 +346,11 @@ watch(
}, },
}, },
] ]
await load() await list_files()
}, },
{ immediate: true }, { immediate: true },
) )
// 监听refreshPending变化
watch(
() => inProps.refreshpending,
async () => {
if (inProps.refreshpending) {
await load()
emit('refreshed')
}
},
)
// 调用API识别 // 调用API识别
async function recognize(path: string) { async function recognize(path: string) {
try { try {
@@ -427,7 +416,7 @@ function stopLoadingProgress() {
} }
onMounted(() => { onMounted(() => {
load() list_files()
}) })
</script> </script>
@@ -447,13 +436,13 @@ onMounted(() => {
rounded="0" rounded="0"
/> />
<VSpacer v-if="isFile" /> <VSpacer v-if="isFile" />
<IconBtn v-if="isFile" @click="recognize(inProps.path || '')"> <IconBtn v-if="isFile" @click="recognize(inProps.item.path || '')">
<VIcon color="primary"> mdi-text-recognition </VIcon> <VIcon color="primary"> mdi-text-recognition </VIcon>
</IconBtn> </IconBtn>
<IconBtn v-if="isFile && items.length > 0" @click="download(items[0])"> <IconBtn v-if="isFile && items.length > 0" @click="download(items[0])">
<VIcon color="primary"> mdi-download </VIcon> <VIcon color="primary"> mdi-download </VIcon>
</IconBtn> </IconBtn>
<IconBtn v-if="!isFile" @click="load"> <IconBtn v-if="!isFile" @click="list_files">
<VIcon color="primary"> mdi-refresh </VIcon> <VIcon color="primary"> mdi-refresh </VIcon>
</IconBtn> </IconBtn>
</VToolbar> </VToolbar>
@@ -506,7 +495,7 @@ onMounted(() => {
{{ formatBytes(item.size) }} {{ formatBytes(item.size) }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<IconBtn class="d-sm-none"> <IconBtn v-if="display.smAndDown.value">
<VIcon icon="mdi-dots-vertical" /> <VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click> <VMenu activator="parent" close-on-content-click>
<VList> <VList>
@@ -526,38 +515,38 @@ onMounted(() => {
</VList> </VList>
</VMenu> </VMenu>
</IconBtn> </IconBtn>
<span v-if="hover.isHovering" class="flex"> <span v-if="hover.isHovering && display.mdAndUp" class="flex">
<VTooltip text="识别"> <VTooltip text="识别">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn v-bind="props" class="d-none d-sm-block" @click.stop="recognize(item.path)"> <IconBtn v-bind="props" @click.stop="recognize(item.path)">
<VIcon icon="mdi-text-recognition" /> <VIcon icon="mdi-text-recognition" />
</IconBtn> </IconBtn>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip text="刮削" v-if="storage == 'local'"> <VTooltip text="刮削" v-if="storage == 'local'">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn v-bind="props" class="d-none d-sm-block" @click.stop="scrape(item.path)"> <IconBtn v-bind="props" @click.stop="scrape(item.path)">
<VIcon icon="mdi-auto-fix" /> <VIcon icon="mdi-auto-fix" />
</IconBtn> </IconBtn>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip text="重命名"> <VTooltip text="重命名">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn v-bind="props" class="d-none d-sm-block" @click.stop="showRenmae(item)"> <IconBtn v-bind="props" @click.stop="showRenmae(item)">
<VIcon icon="mdi-rename" /> <VIcon icon="mdi-rename" />
</IconBtn> </IconBtn>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip text="整理" v-if="storage == 'local'"> <VTooltip text="整理" v-if="storage == 'local'">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn v-bind="props" class="d-none d-sm-block" @click.stop="showTransfer(item)"> <IconBtn v-bind="props" @click.stop="showTransfer(item)">
<VIcon icon="mdi-folder-arrow-right" /> <VIcon icon="mdi-folder-arrow-right" />
</IconBtn> </IconBtn>
</template> </template>
</VTooltip> </VTooltip>
<VTooltip text="删除"> <VTooltip text="删除">
<template #activator="{ props }"> <template #activator="{ props }">
<IconBtn v-bind="props" class="d-none d-sm-block" @click.stop="deleteItem(item)"> <IconBtn v-bind="props" @click.stop="deleteItem(item)">
<VIcon icon="mdi-delete-outline" color="error" /> <VIcon icon="mdi-delete-outline" color="error" />
</IconBtn> </IconBtn>
</template> </template>
@@ -609,7 +598,7 @@ onMounted(() => {
@done=" @done="
() => { () => {
transferPopper = false transferPopper = false
load() list_files()
} }
" "
@close="transferPopper = false" @close="transferPopper = false"
+24 -13
View File
@@ -1,13 +1,19 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Axios } from 'axios' import type { Axios, AxiosRequestConfig } from 'axios'
import type { EndPoints } from '@/api/types' import type { EndPoints, FileItem } from '@/api/types'
import { useDisplay } from 'vuetify'
// 显示器宽度
const display = useDisplay()
// 输入参数 // 输入参数
const inProps = defineProps({ const inProps = defineProps({
storages: Array as PropType<any[]>, storages: Array as PropType<any[]>,
storage: String, storage: String,
path: String, item: {
fileid: String, type: Object as PropType<FileItem>,
required: true,
},
fileidstack: { fileidstack: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: () => [], default: () => [],
@@ -42,8 +48,8 @@ function changeSort() {
// 计算PATH面包屑 // 计算PATH面包屑
const pathSegments = computed(() => { const pathSegments = computed(() => {
let path_str = '' let path_str = ''
const isFolder = inProps.path?.endsWith('/') const isFolder = inProps.item.path?.endsWith('/')
const segments = inProps.path?.split('/').filter(item => item) const segments = inProps.item.path?.split('/').filter(item => item)
const fileids = inProps.fileidstack ?? [] const fileids = inProps.fileidstack ?? []
return ( return (
segments?.map((item, index) => { segments?.map((item, index) => {
@@ -57,6 +63,7 @@ const pathSegments = computed(() => {
) )
}) })
// 当前存储
const storageObject = computed(() => { const storageObject = computed(() => {
return inProps.storages?.find(item => item.code === inProps.storage) return inProps.storages?.find(item => item.code === inProps.storage)
}) })
@@ -89,12 +96,12 @@ async function mkdir() {
emit('loading', true) emit('loading', true)
const url = inProps.endpoints?.mkdir.url const url = inProps.endpoints?.mkdir.url
.replace(/{storage}/g, inProps.storage) .replace(/{storage}/g, inProps.storage)
.replace(/{path}/g, encodeURIComponent(inProps.path + newFolderName.value)) .replace(/{name}/g, newFolderName.value)
.replace(/{fileid}/g, inProps.fileid || '')
const config = { const config: AxiosRequestConfig<FileItem> = {
url, url,
method: inProps.endpoints?.mkdir.method || 'post', method: inProps.endpoints?.mkdir.method || 'post',
data: inProps.item,
} }
// 调API // 调API
@@ -138,15 +145,16 @@ const sortIcon = computed(() => {
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
<VBtn variant="text" :input-value="path === '/'" class="px-1" @click="changePath('/', 'root')"> <VBtn variant="text" :input-value="item.path === '/'" class="px-1" @click="changePath('/', 'root')">
<VIcon :icon="storageObject?.icon" class="mr-2" /> <VIcon :icon="storageObject?.icon" class="mr-2" />
{{ storageObject?.name }} {{ storageObject?.name }}
</VBtn> </VBtn>
<template v-for="(segment, index) in pathSegments" :key="index"> <template v-for="(segment, index) in pathSegments" :key="index">
<VBtn <VBtn
v-if="display.mdAndUp.value"
variant="text" variant="text"
:input-value="index === pathSegments.length - 1" :input-value="index === pathSegments.length - 1"
class="px-1 d-none d-md-block" class="px-1"
@click="changePath(segment.path, inProps.fileidstack[index + 1])" @click="changePath(segment.path, inProps.fileidstack[index + 1])"
> >
<VIcon icon=" mdi-chevron-right" /> <VIcon icon=" mdi-chevron-right" />
@@ -180,13 +188,16 @@ const sortIcon = computed(() => {
</IconBtn> </IconBtn>
</template> </template>
<VCard title="新建文件夹"> <VCard title="新建文件夹">
<DialogCloseBtn @click="newFolderPopper = false" />
<VDivider />
<VCardText> <VCardText>
<VTextField v-model="newFolderName" label="名称" /> <VTextField v-model="newFolderName" label="名称" />
</VCardText> </VCardText>
<VCardActions> <VCardActions>
<div class="flex-grow-1" /> <div class="flex-grow-1" />
<VBtn depressed @click="newFolderPopper = false"> 取消 </VBtn> <VBtn :disabled="!newFolderName" variant="elevated" @click="mkdir" prepend-icon="mdi-check" class="px-5 me-3">
<VBtn :disabled="!newFolderName" depressed variant="tonal" @click="mkdir"> 新建 </VBtn> 新建
</VBtn>
</VCardActions> </VCardActions>
</VCard> </VCard>
</VDialog> </VDialog>
+6 -2
View File
@@ -1,5 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import SlideViewTitle from '@/components/slide/SlideViewTitle.vue' import SlideViewTitle from '@/components/slide/SlideViewTitle.vue'
import { useDisplay } from 'vuetify'
// 显示器宽度
const display = useDisplay()
// 元素 // 元素
const slideview_content = ref() const slideview_content = ref()
@@ -91,7 +95,7 @@ onActivated(() => {
<slot name="title"> <slot name="title">
<SlideViewTitle /> <SlideViewTitle />
</slot> </slot>
<div v-if="disabled !== 3" class="me-1 d-none d-md-flex"> <div v-if="disabled !== 3 && display.mdAndUp.value" class="me-1 d-flex">
<VBtn <VBtn
class="rounded-circle" class="rounded-circle"
variant="text" variant="text"
@@ -122,8 +126,8 @@ onActivated(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.slideview_content { .slideview_content {
-ms-overflow-style: none !important;
overflow: scroll hidden !important; overflow: scroll hidden !important;
-ms-overflow-style: none !important;
overscroll-behavior-x: contain !important; overscroll-behavior-x: contain !important;
scrollbar-width: none !important; scrollbar-width: none !important;
} }
+1 -2
View File
@@ -71,7 +71,6 @@ function initOptions(data: Context) {
optionValue(resolutionFilterOptions.value, meta_info?.resource_pix) optionValue(resolutionFilterOptions.value, meta_info?.resource_pix)
} }
// 对季过滤选项进行排序 // 对季过滤选项进行排序
const sortSeasonFilterOptions = computed(() => { const sortSeasonFilterOptions = computed(() => {
return seasonFilterOptions.value.sort((a, b) => { return seasonFilterOptions.value.sort((a, b) => {
@@ -182,7 +181,7 @@ onMounted(() => {
</VVirtualScroll> </VVirtualScroll>
</VList> </VList>
</VCol> </VCol>
<VCol xl="2" md="3" class="d-none d-md-block"> <VCol xl="2" md="3" v-if="display.mdAndUp.value">
<VList <VList
lines="one" lines="one"
class="rounded shadow-lg" class="rounded shadow-lg"
+27 -25
View File
@@ -6,28 +6,28 @@ import store from '@/store'
const endpoints = { const endpoints = {
list: { list: {
url: '/{storage}/list?path={path}&sort={sort}&fileid={fileid}&filetype={filetype}&pickcode={pickcode}', url: '/{storage}/list?sort={sort}',
method: 'get', method: 'post',
}, },
mkdir: { mkdir: {
url: '/{storage}/mkdir?path={path}&fileid={fileid}', url: '/{storage}/mkdir?name={name}',
method: 'get', method: 'post',
}, },
delete: { delete: {
url: '/{storage}/delete?path={path}&fileid={fileid}', url: '/{storage}/delete',
method: 'get', method: 'post',
}, },
download: { download: {
url: '/{storage}/download?path={path}&fileid={fileid}&pickcode={pickcode}', url: '/{storage}/download',
method: 'get', method: 'get',
}, },
image: { image: {
url: '/{storage}/image?path={path}&fileid={fileid}&pickcode={pickcode}', url: '/{storage}/image',
method: 'get', method: 'get',
}, },
rename: { rename: {
url: '/{storage}/rename?path={path}&new_name={newname}&fileid={fileid}&filetype={filetype}', url: '/{storage}/rename?new_name={newname}',
method: 'get', method: 'post',
}, },
} }
@@ -36,14 +36,13 @@ const user_level = store.state.auth.level
// 用户存储 // 用户存储
const userStorage = user_level > 1 ? 'local,aliyun,u115' : 'local' const userStorage = user_level > 1 ? 'local,aliyun,u115' : 'local'
// 当前目录 // 当前文件项
const path = ref<string>('') const operItem = ref<FileItem>({
type: 'dir',
// 当前fileid name: '/',
const fileid = ref<string>('root') path: '/',
fileid: 'root',
// 当前pickcode })
const pickcode = ref<string>('')
// fileid的堆栈 // fileid的堆栈
const fileidstack = ref<string[]>(['root']) const fileidstack = ref<string[]>(['root'])
@@ -91,7 +90,13 @@ async function loadDownloadDirectories() {
const result: { [key: string]: any } = await api.get('system/setting/DownloadDirectories') const result: { [key: string]: any } = await api.get('system/setting/DownloadDirectories')
if (result.success && result.data?.value) { if (result.success && result.data?.value) {
downloadDirectories.value = result.data.value downloadDirectories.value = result.data.value
path.value = findCommonPath(downloadDirectories.value.map(item => item.path) as string[]) const path = findCommonPath(downloadDirectories.value.map(item => item.path) as string[])
const name = path.split('/').filter(Boolean).pop() ?? ''
operItem.value = {
type: 'dir',
name: name,
path: path,
}
} }
} catch (error) { } catch (error) {
console.log(error) console.log(error)
@@ -100,10 +105,8 @@ async function loadDownloadDirectories() {
// 目录变化 // 目录变化
function pathChanged(item: FileItem) { function pathChanged(item: FileItem) {
path.value = item.path operItem.value = item
pickcode.value = item.pickcode || ''
if (item.fileid) { if (item.fileid) {
fileid.value = item.fileid
if (fileidstack.value.includes(item.fileid)) { if (fileidstack.value.includes(item.fileid)) {
fileidstack.value = fileidstack.value.slice(0, fileidstack.value.indexOf(item.fileid) + 1) fileidstack.value = fileidstack.value.slice(0, fileidstack.value.indexOf(item.fileid) + 1)
} else { } else {
@@ -112,6 +115,7 @@ function pathChanged(item: FileItem) {
} }
} }
// 加载初始目录
onBeforeMount(loadDownloadDirectories) onBeforeMount(loadDownloadDirectories)
</script> </script>
@@ -120,12 +124,10 @@ onBeforeMount(loadDownloadDirectories)
<FileBrowser <FileBrowser
:storages="userStorage" :storages="userStorage"
:tree="false" :tree="false"
:path="path"
:fileid="fileid"
:pickcode="pickcode"
:fileidstack="fileidstack" :fileidstack="fileidstack"
:endpoints="endpoints" :endpoints="endpoints"
:axios="api" :axios="api"
:item="operItem"
@pathchanged="pathChanged" @pathchanged="pathChanged"
/> />
</div> </div>
+9 -3
View File
@@ -6,6 +6,10 @@ import { requiredValidator } from '@/@validators'
import api from '@/api' import api from '@/api'
import type { User } from '@/api/types' import type { User } from '@/api/types'
import avatar1 from '@images/avatars/avatar-1.png' import avatar1 from '@images/avatars/avatar-1.png'
import { useDisplay } from 'vuetify'
// 显示器宽度
const display = useDisplay()
const isNewPasswordVisible = ref(false) const isNewPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false) const isConfirmPasswordVisible = ref(false)
@@ -250,7 +254,7 @@ onMounted(() => {
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
<VBtn color="primary" @click="refInputEl?.click()"> <VBtn color="primary" @click="refInputEl?.click()">
<VIcon icon="mdi-cloud-upload-outline" /> <VIcon icon="mdi-cloud-upload-outline" />
<span class="d-none d-sm-block ms-2">上传头像</span> <span v-if="display.mdAndUp.value" class="ms-2">上传头像</span>
</VBtn> </VBtn>
<input <input
@@ -264,7 +268,7 @@ onMounted(() => {
<VBtn type="reset" color="error" variant="tonal" @click="resetAvatar"> <VBtn type="reset" color="error" variant="tonal" @click="resetAvatar">
<VIcon icon="mdi-refresh" /> <VIcon icon="mdi-refresh" />
<span class="d-none d-sm-block ms-2">重置</span> <span v-if="display.mdAndUp.value" class="ms-2">重置</span>
</VBtn> </VBtn>
<VBtn <VBtn
@@ -273,7 +277,9 @@ onMounted(() => {
@click.stop="accountInfo.is_otp ? disableOtp() : getOtpUri()" @click.stop="accountInfo.is_otp ? disableOtp() : getOtpUri()"
> >
<VIcon icon="mdi-account-key" /> <VIcon icon="mdi-account-key" />
<span class="d-none d-sm-block ms-2">{{ accountInfo.is_otp ? '关闭验证' : '双重验证' }}</span> <span v-if="display.mdAndUp.value" class="ms-2">{{
accountInfo.is_otp ? '关闭验证' : '双重验证'
}}</span>
</VBtn> </VBtn>
</div> </div>