refactor(components): 将目录对话框提取为独立组件

提取原CreateTemplateScreen中的目录对话框逻辑到独立的DirectoryDialog组件
简化主屏幕代码,提高可维护性和复用性
This commit is contained in:
zhanggy committed 2025-10-31 10:39:58 +08:00
1 parent ae20a30616
commit 0080de8e0d
2 files changed
+150 -79

No files matched your search

+124
View File
@@ -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>
+26 -79
View File
@@ -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"
:is-edit-mode="!!currentEditingItem"
:parent-directory="parentDirectoryForNewItems"
:editing-item="currentEditingItem"
@confirm="handleDirectoryDialogConfirm"
/>
</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>
<!-- 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()