File size: 5,735 Bytes
8ede856 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | <template>
<v-dialog v-model="showDialog" max-width="500px" persistent>
<v-card>
<v-card-title>
<v-icon class="mr-2">mdi-folder-move</v-icon>
{{ labels.title }}
</v-card-title>
<v-card-text>
<p class="text-body-2 text-medium-emphasis mb-4">
{{ labels.description }}
</p>
<!-- 文件夹选择树 -->
<div class="folder-select-tree">
<v-list density="compact" nav class="tree-list">
<!-- 根目录选项 -->
<v-list-item :active="selectedFolderId === null" @click="selectFolder(null)" rounded="lg"
class="mb-1">
<template v-slot:prepend>
<v-icon>mdi-home</v-icon>
</template>
<v-list-item-title>{{ labels.rootFolder }}</v-list-item-title>
</v-list-item>
<!-- 文件夹树 -->
<template v-if="!treeLoading">
<BaseMoveTargetNode v-for="folder in folderTree" :key="folder.folder_id" :folder="folder"
:depth="0" :selected-folder-id="selectedFolderId" :disabled-folder-ids="disabledFolderIds"
@select="selectFolder" />
</template>
<!-- 加载状态 -->
<div v-if="treeLoading" class="text-center pa-4">
<v-progress-circular indeterminate size="24" />
</div>
</v-list>
</div>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn variant="text" @click="closeDialog">
{{ labels.cancelButton }}
</v-btn>
<v-btn color="primary" variant="flat" @click="submitMove" :loading="loading">
{{ labels.moveButton }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import { defineComponent, type PropType } from 'vue';
import type { FolderTreeNode } from './types';
import BaseMoveTargetNode from './BaseMoveTargetNode.vue';
import { collectFolderAndChildrenIds } from './useFolderManager';
interface DefaultLabels {
title: string;
description: string;
rootFolder: string;
cancelButton: string;
moveButton: string;
}
const defaultLabels: DefaultLabels = {
title: '移动到文件夹',
description: '选择目标文件夹',
rootFolder: '根目录',
cancelButton: '取消',
moveButton: '移动'
};
export default defineComponent({
name: 'BaseMoveToFolderDialog',
components: {
BaseMoveTargetNode
},
props: {
modelValue: {
type: Boolean,
default: false
},
folderTree: {
type: Array as PropType<FolderTreeNode[]>,
required: true
},
treeLoading: {
type: Boolean,
default: false
},
// 当移动的是文件夹时,需要传入当前文件夹 ID 以禁用自身和子文件夹
currentFolderId: {
type: String as PropType<string | null>,
default: null
},
// 项目当前所在的文件夹 ID(用于初始化选择)
itemCurrentFolderId: {
type: String as PropType<string | null>,
default: null
},
// 是否是移动文件夹(如果是,需要禁用自身和子文件夹)
isMovingFolder: {
type: Boolean,
default: false
},
labels: {
type: Object as PropType<Partial<DefaultLabels>>,
default: () => ({})
}
},
emits: ['update:modelValue', 'move'],
data() {
return {
selectedFolderId: null as string | null,
loading: false
};
},
computed: {
showDialog: {
get(): boolean {
return this.modelValue;
},
set(value: boolean) {
this.$emit('update:modelValue', value);
}
},
mergedLabels(): DefaultLabels {
return { ...defaultLabels, ...this.labels };
},
// 禁用的文件夹 ID(不能移动到自己或子文件夹)
disabledFolderIds(): string[] {
if (!this.isMovingFolder || !this.currentFolderId) return [];
return collectFolderAndChildrenIds(this.folderTree, this.currentFolderId);
}
},
watch: {
modelValue(newValue: boolean) {
if (newValue) {
// 初始化选中为当前所在文件夹
this.selectedFolderId = this.itemCurrentFolderId;
}
}
},
methods: {
selectFolder(folderId: string | null) {
// 检查是否禁用
if (folderId && this.disabledFolderIds.includes(folderId)) return;
this.selectedFolderId = folderId;
},
closeDialog() {
this.showDialog = false;
},
submitMove() {
this.$emit('move', this.selectedFolderId);
},
setLoading(value: boolean) {
this.loading = value;
}
}
});
</script>
<style scoped>
.folder-select-tree {
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
border-radius: 8px;
}
.tree-list {
padding: 8px;
}
</style>
|