refactor(components): 将目录对话框提取为独立组件
提取原CreateTemplateScreen中的目录对话框逻辑到独立的DirectoryDialog组件 简化主屏幕代码,提高可维护性和复用性
This commit is contained in:
1 parent
ae20a30616
commit
0080de8e0d
2 files changed
+151
-80
No files matched your search
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<v-dialog
|
||||
v-model="isVisible"
|
||||
max-width="500"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ isEditMode ? '编辑目录' : '添加目录' }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="formValid">
|
||||
<v-text-field
|
||||
v-model="directoryPath"
|
||||
label="目录名称"
|
||||
placeholder="请输入目录名称"
|
||||
:rules="[requiredRule('目录名称')]"
|
||||
required
|
||||
density="comfortable"
|
||||
ref="inputRef"
|
||||
@keydown.enter="confirm"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="close">取消</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirm"
|
||||
:disabled="!formValid"
|
||||
>
|
||||
{{ isEditMode ? '保存' : '添加' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
import { TreeNode } from '@/renderer/store/template'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
isEditMode?: boolean
|
||||
parentDirectory?: string | null
|
||||
editingItem?: TreeNode | null
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm', data: { path: string; fullPath: string }): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isEditMode: false,
|
||||
parentDirectory: null,
|
||||
editingItem: null
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
// Internal state
|
||||
const isVisible = ref(false)
|
||||
const formValid = ref(false)
|
||||
const directoryPath = ref('')
|
||||
const inputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
// Computed
|
||||
const isEditMode = computed(() => props.isEditMode)
|
||||
|
||||
// Watch for visibility changes
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
isVisible.value = newValue
|
||||
if (newValue) {
|
||||
// Reset form when dialog opens
|
||||
directoryPath.value = props.editingItem?.path || ''
|
||||
autoFocusInput()
|
||||
}
|
||||
})
|
||||
|
||||
// Watch for internal visibility changes and emit update
|
||||
watch(isVisible, (newValue) => {
|
||||
emit('update:modelValue', newValue)
|
||||
})
|
||||
|
||||
// Validation rules
|
||||
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
|
||||
!!v || `${fieldName}为必填项`
|
||||
|
||||
// Methods
|
||||
const autoFocusInput = async () => {
|
||||
await nextTick()
|
||||
if (inputRef.value) {
|
||||
;(inputRef.value as HTMLInputElement).focus()
|
||||
}
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
if (!directoryPath.value.trim()) return
|
||||
|
||||
// Construct the full path if there's a parent directory
|
||||
let fullPath = directoryPath.value
|
||||
if (props.parentDirectory) {
|
||||
// Make sure parent directory ends with a slash before appending
|
||||
const parentPath = props.parentDirectory.endsWith('/')
|
||||
? props.parentDirectory
|
||||
: props.parentDirectory + '/'
|
||||
fullPath = parentPath + directoryPath.value
|
||||
}
|
||||
|
||||
emit('confirm', {
|
||||
path: directoryPath.value,
|
||||
fullPath
|
||||
})
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
isVisible.value = false
|
||||
directoryPath.value = ''
|
||||
}
|
||||
</script>
|
||||
@@ -151,42 +151,14 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Add Directory Dialog -->
|
||||
<v-dialog
|
||||
<!-- Directory Dialog -->
|
||||
<DirectoryDialog
|
||||
v-model="showDirectoryDialog"
|
||||
max-width="500"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
{{ currentEditingItem ? '编辑目录' : '添加目录' }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="directoryFormValid">
|
||||
<v-text-field
|
||||
v-model="newDirectoryPath"
|
||||
:label="'目录名称'"
|
||||
:placeholder="'请输入目录名称'"
|
||||
:rules="[requiredRule('目录名称')]"
|
||||
required
|
||||
density="comfortable"
|
||||
ref="directoryInputRef"
|
||||
@keydown.enter="confirmAddDirectory"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="closeDirectoryDialog"> 取消 </v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="confirmAddDirectory"
|
||||
:disabled="!directoryFormValid"
|
||||
>
|
||||
{{ currentEditingItem ? '保存' : '添加' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
:is-edit-mode="!!currentEditingItem"
|
||||
:parent-directory="parentDirectoryForNewItems"
|
||||
:editing-item="currentEditingItem"
|
||||
@confirm="handleDirectoryDialogConfirm"
|
||||
/>
|
||||
|
||||
<!-- Add File Dialog -->
|
||||
<v-dialog
|
||||
@@ -265,6 +237,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { useTemplateStore, TreeNode } from '@/renderer/store/template'
|
||||
import { useSnackbarStore } from '@/renderer/store/snackbar'
|
||||
import TemplateTree from '@/renderer/components/TemplateTree.vue'
|
||||
import DirectoryDialog from '@/renderer/components/DirectoryDialog.vue'
|
||||
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -282,21 +255,22 @@ const templateDescription = ref('')
|
||||
const hasChanges = ref(false)
|
||||
|
||||
// Dialogs
|
||||
const showDirectoryDialog = ref(false)
|
||||
const showFileDialog = ref(false)
|
||||
const directoryFormValid = ref(false)
|
||||
const fileFormValid = ref(false)
|
||||
|
||||
// Input refs for auto-focus
|
||||
const directoryInputRef = ref<HTMLInputElement | null>(null)
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
// Directory dialog state
|
||||
const showDirectoryDialog = ref(false)
|
||||
const directoryFormValid = ref(false)
|
||||
const parentDirectoryForNewItems = ref<string | null>(null)
|
||||
const currentEditingItem = ref<TreeNode | null>(null)
|
||||
|
||||
|
||||
|
||||
// New item state
|
||||
const newDirectoryPath = ref('')
|
||||
const newFilePath = ref('')
|
||||
const parentDirectoryForNewItems = ref<string | null>(null) // Track parent context
|
||||
const selectedSourcePath = ref<string>('') // Track the selected source file or folder
|
||||
|
||||
// Tree state
|
||||
@@ -390,9 +364,9 @@ const updateTreeHeight = () => {
|
||||
}
|
||||
|
||||
const addRootDirectory = () => {
|
||||
newDirectoryPath.value = ''
|
||||
parentDirectoryForNewItems.value = null
|
||||
currentEditingItem.value = null
|
||||
showDirectoryDialog.value = true
|
||||
autoFocusDirectoryInput()
|
||||
}
|
||||
|
||||
const addRootFile = () => {
|
||||
@@ -413,23 +387,12 @@ const addFileToDirectory = (directoryItem: TreeItem) => {
|
||||
|
||||
const addNewItemToCurrentDirectory = (directoryItem: TreeItem) => {
|
||||
parentDirectoryForNewItems.value = directoryItem.name // Store parent context
|
||||
newDirectoryPath.value = '' // Don't pre-fill the path
|
||||
currentEditingItem.value = null
|
||||
showDirectoryDialog.value = true
|
||||
autoFocusDirectoryInput()
|
||||
}
|
||||
|
||||
const confirmAddDirectory = () => {
|
||||
if (!newDirectoryPath.value.trim()) return
|
||||
|
||||
// Construct the full path if there's a parent directory
|
||||
let fullPath = newDirectoryPath.value
|
||||
if (parentDirectoryForNewItems.value) {
|
||||
// Make sure parent directory ends with a slash before appending
|
||||
const parentPath = parentDirectoryForNewItems.value.endsWith('/')
|
||||
? parentDirectoryForNewItems.value
|
||||
: parentDirectoryForNewItems.value + '/'
|
||||
fullPath = parentPath + newDirectoryPath.value
|
||||
}
|
||||
const handleDirectoryDialogConfirm = (data: { path: string; fullPath: string }) => {
|
||||
const { path, fullPath } = data
|
||||
|
||||
// Check for duplicate names in the same parent directory
|
||||
if (parentDirectoryForNewItems.value) {
|
||||
@@ -440,7 +403,7 @@ const confirmAddDirectory = () => {
|
||||
)
|
||||
if (parentNode && parentNode.children) {
|
||||
const duplicateExists = parentNode.children.some(
|
||||
(child) => child.name === newDirectoryPath.value
|
||||
(child) => child.name === path
|
||||
)
|
||||
if (duplicateExists) {
|
||||
snackbar.error('当前目录下已存在同名节点!')
|
||||
@@ -450,7 +413,7 @@ const confirmAddDirectory = () => {
|
||||
} else {
|
||||
// Check at root level for duplicates
|
||||
const duplicateExists = treeStructure.value.some(
|
||||
(node) => node.name === newDirectoryPath.value
|
||||
(node) => node.name === path
|
||||
)
|
||||
if (duplicateExists) {
|
||||
snackbar.error('根目录下已存在同名节点!')
|
||||
@@ -468,7 +431,7 @@ const confirmAddDirectory = () => {
|
||||
// Find and update the existing directory in the tree
|
||||
const updatedNode: TreeNode = {
|
||||
id: currentEditingItem.value.id,
|
||||
name: newDirectoryPath.value,
|
||||
name: path,
|
||||
path: fullPath,
|
||||
type: 'directory'
|
||||
}
|
||||
@@ -481,7 +444,7 @@ const confirmAddDirectory = () => {
|
||||
// Create new directory node
|
||||
const newNode: TreeNode = {
|
||||
id: newId,
|
||||
name: newDirectoryPath.value,
|
||||
name: path,
|
||||
path: fullPath,
|
||||
type: 'directory'
|
||||
}
|
||||
@@ -508,8 +471,6 @@ const confirmAddDirectory = () => {
|
||||
nextTick(() => {
|
||||
expandAll()
|
||||
})
|
||||
|
||||
newDirectoryPath.value = ''
|
||||
}
|
||||
|
||||
// 在树状结构中更新节点的辅助函数
|
||||
@@ -560,13 +521,7 @@ const addChildToNode = (
|
||||
})
|
||||
}
|
||||
|
||||
// Functions to close dialogs and reset state
|
||||
const closeDirectoryDialog = () => {
|
||||
showDirectoryDialog.value = false
|
||||
newDirectoryPath.value = ''
|
||||
parentDirectoryForNewItems.value = null
|
||||
currentEditingItem.value = null
|
||||
}
|
||||
|
||||
|
||||
const closeFileDialog = () => {
|
||||
showFileDialog.value = false
|
||||
@@ -846,9 +801,8 @@ const findNodeInTreeByName = (
|
||||
const showEditDirectoryDialog = (directory: TreeNode) => {
|
||||
// Set up for editing an existing directory
|
||||
currentEditingItem.value = directory
|
||||
newDirectoryPath.value = directory.path // Set the current path for editing
|
||||
parentDirectoryForNewItems.value = null
|
||||
showDirectoryDialog.value = true
|
||||
autoFocusDirectoryInput()
|
||||
}
|
||||
|
||||
// Show dialog to edit file
|
||||
@@ -866,8 +820,7 @@ const showEditFileDialog = (file: TreeNode) => {
|
||||
autoFocusFileInput()
|
||||
}
|
||||
|
||||
// Variable to track the current item being edited
|
||||
const currentEditingItem = ref<TreeNode | null>(null)
|
||||
|
||||
|
||||
const deleteItem = async (item: TreeItem) => {
|
||||
const confirmed = await ConfirmDialogService.confirm({
|
||||
@@ -911,13 +864,7 @@ const selectSourceFileOrFolder = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Auto focus methods
|
||||
const autoFocusDirectoryInput = async () => {
|
||||
await nextTick()
|
||||
if (directoryInputRef.value) {
|
||||
;(directoryInputRef.value as HTMLInputElement).focus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const autoFocusFileInput = async () => {
|
||||
await nextTick()
|
||||
|
||||
Reference in new issue
Block a user