refactor(模板创建): 重构模板树结构处理逻辑

- 将 parentDirectory 类型从 string 改为 TreeNode 以直接引用节点
- 移除冗余的 TreeItem 接口和转换逻辑
- 简化文件/目录添加和更新逻辑,直接操作 TreeNode
- 使用新的 generateUniqueId 工具函数生成唯一ID
- 移除不必要的路径拼接和重复检查逻辑
This commit is contained in:
zhanggy committed 2025-10-31 13:41:45 +08:00
1 parent d3f4242445
commit 47fb017e21
4 files changed
+78 -207

No files matched your search

+1 -1
View File
@@ -44,7 +44,7 @@ import { TreeNode } from '@/renderer/store/template'
interface Props { interface Props {
modelValue: boolean modelValue: boolean
isEditMode?: boolean isEditMode?: boolean
parentDirectory?: string | null parentDirectory?: TreeNode | null
editingItem?: TreeNode | null editingItem?: TreeNode | null
} }
+2 -12
View File
@@ -70,13 +70,13 @@ import { TreeNode } from '@/renderer/store/template'
interface Props { interface Props {
modelValue: boolean modelValue: boolean
isEditMode?: boolean isEditMode?: boolean
parentDirectory?: string | null parentDirectory?: TreeNode | null
editingItem?: TreeNode | null editingItem?: TreeNode | null
} }
interface Emits { interface Emits {
(e: 'update:modelValue', value: boolean): void (e: 'update:modelValue', value: boolean): void
(e: 'confirm', data: { path: string; fullPath: string; sourceFilePath?: string }): void (e: 'confirm', data: { path: string; sourceFilePath?: string }): void
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@@ -137,19 +137,9 @@ const selectSourceFileOrFolder = async () => {
const confirm = () => { const confirm = () => {
if (!filePath.value.trim()) return if (!filePath.value.trim()) return
// Construct the full path if there's a parent directory
let fullPath = filePath.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 + filePath.value
}
emit('confirm', { emit('confirm', {
path: filePath.value, path: filePath.value,
fullPath,
sourceFilePath: selectedSourcePath.value || undefined sourceFilePath: selectedSourcePath.value || undefined
}) })
+64 -194
View File
@@ -111,7 +111,7 @@
</div> </div>
<TemplateTree <TemplateTree
:items="treeItems" :items="treeStructure"
:height="treeHeight" :height="treeHeight"
v-model:expanded="expandedNodes" v-model:expanded="expandedNodes"
:empty-text="'暂无文件或目录'" :empty-text="'暂无文件或目录'"
@@ -152,6 +152,7 @@ import TemplateTree from '@/renderer/components/TemplateTree.vue'
import DirectoryDialog from '@/renderer/components/DirectoryDialog.vue' import DirectoryDialog from '@/renderer/components/DirectoryDialog.vue'
import FileDialog from '@/renderer/components/FileDialog.vue' import FileDialog from '@/renderer/components/FileDialog.vue'
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service' import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
import { generateUniqueId } from '@/renderer/utils'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@@ -172,7 +173,7 @@ const showFileDialog = ref(false)
// Directory dialog state // Directory dialog state
const showDirectoryDialog = ref(false) const showDirectoryDialog = ref(false)
const parentDirectoryForNewItems = ref<string | null>(null) const parentDirectoryForNewItems = ref<TreeNode | null>(null)
const currentEditingItem = ref<TreeNode | null>(null) const currentEditingItem = ref<TreeNode | null>(null)
@@ -180,38 +181,15 @@ const currentEditingItem = ref<TreeNode | null>(null)
const expandedNodes = ref<string[]>([]) const expandedNodes = ref<string[]>([])
const treeHeight = ref(600) const treeHeight = ref(600)
// Define type for tree items
interface TreeItem {
id: string
name: string
type: 'directory' | 'file'
children?: TreeItem[]
}
// Item state - 使用树状结构 // Item state - 使用树状结构
const treeStructure = ref<TreeNode[]>([]) const treeStructure = ref<TreeNode[]>([])
// Computed // Computed
const isEditMode = computed(() => !!route.params.id) const isEditMode = computed(() => !!route.params.id)
const treeItems = computed<TreeItem[]>(() => {
// 直接返回树状结构
return treeStructure.value.map((node) => convertToTreeItem(node))
})
// 转换 TreeNode 到 TreeItem 的辅助函数
const convertToTreeItem = (node: TreeNode): TreeItem => {
const treeItem: TreeItem = {
id: node.id,
name: node.name,
type: node.type
}
if (node.children && node.children.length > 0) {
treeItem.children = node.children.map((child) => convertToTreeItem(child))
}
return treeItem
}
// Validation rules // Validation rules
const requiredRule = (fieldName: string) => (v: string | null | undefined) => const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
@@ -278,49 +256,21 @@ const addRootFile = () => {
// Add a file directly to a specific directory // Add a file directly to a specific directory
const addFileToDirectory = (directoryItem: TreeItem) => { const addFileToDirectory = (directoryItem: TreeNode) => {
parentDirectoryForNewItems.value = directoryItem.name // Store the directory as parent parentDirectoryForNewItems.value = directoryItem // Store the directory node as parent
currentEditingItem.value = null currentEditingItem.value = null
showFileDialog.value = true showFileDialog.value = true
} }
const addNewItemToCurrentDirectory = (directoryItem: TreeItem) => { const addNewItemToCurrentDirectory = (directoryItem: TreeNode) => {
parentDirectoryForNewItems.value = directoryItem.name // Store parent context parentDirectoryForNewItems.value = directoryItem // Store the directory node as parent
currentEditingItem.value = null currentEditingItem.value = null
showDirectoryDialog.value = true showDirectoryDialog.value = true
} }
const handleDirectoryDialogConfirm = (data: { path: string }) => { const handleDirectoryDialogConfirm = (data: { path: string }) => {
const { path } = data const { path } = data
const newId = generateUniqueId('dir')
// Check for duplicate names in the same parent directory
if (parentDirectoryForNewItems.value) {
// In the parent directory, check if a node with the same name already exists
const parentNode = findNodeInTreeByName(
treeStructure.value,
parentDirectoryForNewItems.value
)
if (parentNode && parentNode.children) {
const duplicateExists = parentNode.children.some(
(child) => child.name === path
)
if (duplicateExists) {
snackbar.error('当前目录下已存在同名节点!')
return
}
}
} else {
// Check at root level for duplicates
const duplicateExists = treeStructure.value.some(
(node) => node.name === path
)
if (duplicateExists) {
snackbar.error('根目录下已存在同名节点!')
return
}
}
const newId = `dir-${Date.now()}-${Math.random()}`
// Check if we're editing an existing directory // Check if we're editing an existing directory
if ( if (
@@ -329,16 +279,18 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
) { ) {
// Find and update the existing directory in the tree // Find and update the existing directory in the tree
const updatedNode: TreeNode = { const updatedNode: TreeNode = {
id: currentEditingItem.value.id, ...currentEditingItem.value,
name: path, name: path,
path: path, path: path,
type: 'directory' type: 'directory'
} }
console.log('updatedNode', updatedNode)
treeStructure.value = updateNodeInTree( treeStructure.value = updateNodeInTree(
treeStructure.value, treeStructure.value,
currentEditingItem.value.path, currentEditingItem.value.path,
updatedNode updatedNode
) )
console.log('update treeStructure', treeStructure.value)
} else { } else {
// Create new directory node // Create new directory node
const newNode: TreeNode = { const newNode: TreeNode = {
@@ -350,13 +302,29 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
// Add to tree structure // Add to tree structure
if (parentDirectoryForNewItems.value) { if (parentDirectoryForNewItems.value) {
// In the parent directory, check if a node with the same name already exists
const parentNode = parentDirectoryForNewItems.value
if (parentNode.children) {
const duplicateExists = parentNode.children.some(
(child) => child.name === path
)
if (duplicateExists) {
snackbar.error('当前目录下已存在同名节点!')
return
}
}
// Add as child to the parent directory // Add as child to the parent directory
treeStructure.value = addChildToNode( parentNode.children = parentNode.children || []
treeStructure.value, parentNode.children.push(newNode)
parentDirectoryForNewItems.value,
newNode
)
} else { } else {
// Check at root level for duplicates
const duplicateExists = treeStructure.value.some(
(node) => node.name === path
)
if (duplicateExists) {
snackbar.error('根目录下已存在同名节点!')
return
}
// Add as root node // Add as root node
treeStructure.value.push(newNode) treeStructure.value.push(newNode)
} }
@@ -372,45 +340,17 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
// 在树状结构中更新节点的辅助函数 // 在树状结构中更新节点的辅助函数
const updateNodeInTree = ( const updateNodeInTree = (
nodes: TreeNode[], nodes: TreeNode[],
oldPath: string, oldId: string,
updatedNode: TreeNode updatedNode: TreeNode
): TreeNode[] => { ): TreeNode[] => {
return nodes.map((node) => { return nodes.map((node) => {
if (node.path === oldPath) { if (node.id === oldId) {
return updatedNode return updatedNode
} }
if (node.children) { if (node.children) {
return { return {
...node, ...node,
children: updateNodeInTree(node.children, oldPath, updatedNode) children: updateNodeInTree(node.children, oldId, updatedNode)
}
}
return node
})
}
// 向节点添加子节点的辅助函数
const addChildToNode = (
nodes: TreeNode[],
parentName: string,
childNode: TreeNode
): TreeNode[] => {
return nodes.map((node) => {
if (
node.name === parentName ||
(node.path && node.path.endsWith('/' + parentName)) ||
node.path === parentName
) {
const children = node.children || []
return {
...node,
children: [...children, childNode]
}
}
if (node.children) {
return {
...node,
children: addChildToNode(node.children, parentName, childNode)
} }
} }
return node return node
@@ -418,10 +358,12 @@ const addChildToNode = (
} }
const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceFilePath?: string }) => {
const { path, fullPath, sourceFilePath } = data
const newId = `file-${Date.now()}-${Math.random()}`
const handleFileDialogConfirm = (data: { path: string; sourceFilePath?: string }) => {
const { path, sourceFilePath } = data
const newId = generateUniqueId('file')
// Check if we're editing an existing file // Check if we're editing an existing file
if (currentEditingItem.value && currentEditingItem.value.type === 'file') { if (currentEditingItem.value && currentEditingItem.value.type === 'file') {
@@ -429,24 +371,21 @@ const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceF
const updatedNode: TreeNode = { const updatedNode: TreeNode = {
id: currentEditingItem.value.id, id: currentEditingItem.value.id,
name: path, name: path,
path: fullPath, path: path,
type: 'file', type: 'file',
sourceFilePath: sourceFilePath sourceFilePath: sourceFilePath
} }
treeStructure.value = updateNodeInTree( treeStructure.value = updateNodeInTree(
treeStructure.value, treeStructure.value,
currentEditingItem.value.path, currentEditingItem.value.id,
updatedNode updatedNode
) )
} else { } else {
// Check for duplicate names in the same parent directory // Check for duplicate names in the same parent directory
if (parentDirectoryForNewItems.value) { if (parentDirectoryForNewItems.value) {
// In the parent directory, check if a node with the same name already exists // In the parent directory, check if a node with the same name already exists
const parentNode = findNodeInTreeByName( const parentNode = parentDirectoryForNewItems.value
treeStructure.value, if (parentNode.children) {
parentDirectoryForNewItems.value
)
if (parentNode && parentNode.children) {
const duplicateExists = parentNode.children.some( const duplicateExists = parentNode.children.some(
(child) => child.name === path (child) => child.name === path
) )
@@ -466,65 +405,29 @@ const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceF
} }
} }
// Also check for duplicates using the full path (for backward compatibility) const newFileNode: TreeNode = {
const hasDuplicate = checkForDuplicateInTree(treeStructure.value, fullPath) id: newId,
if (hasDuplicate) { name: path,
snackbar.error('该文件路径已存在!') path: path,
return type: 'file',
sourceFilePath
} }
// If we have a selected file, just add it // Add to tree structure
if (sourceFilePath) { if (parentDirectoryForNewItems.value) {
const newFileNode: TreeNode = { // Add as child to the parent directory
id: newId, const parentNode = parentDirectoryForNewItems.value
name: path, parentNode.children = parentNode.children || []
path: fullPath, parentNode.children.push(newFileNode)
type: 'file',
sourceFilePath
}
// Add to tree structure
if (parentDirectoryForNewItems.value) {
// Add as child to the parent directory
treeStructure.value = addChildToNode(
treeStructure.value,
parentDirectoryForNewItems.value,
newFileNode
)
} else {
// Add as root node
treeStructure.value.push(newFileNode)
}
} else { } else {
// Otherwise, show warning // Add as root node
snackbar.warning('请先选择一个文件或文件夹') treeStructure.value.push(newFileNode)
return
} }
} }
showFileDialog.value = false showFileDialog.value = false
parentDirectoryForNewItems.value = null // Clear parent context after creation parentDirectoryForNewItems.value = null // Clear parent context after creation
currentEditingItem.value = null currentEditingItem.value = null
}
// 检查树中是否有重复路径的辅助函数
const checkForDuplicateInTree = (
nodes: TreeNode[],
pathToCheck: string
): boolean => {
for (const node of nodes) {
if (node.path === pathToCheck) {
return true
}
if (node.children) {
if (checkForDuplicateInTree(node.children, pathToCheck)) {
return true
}
}
}
return false
} }
const goBack = async () => { const goBack = async () => {
@@ -547,25 +450,11 @@ const goBack = async () => {
// Unified method to edit items (both files and directories) // Unified method to edit items (both files and directories)
// This function needs to find the actual path of the item in our hierarchical tree const editItem = (item: TreeNode) => {
const editItem = (item: TreeItem) => { if (item.type === 'directory') {
// Find the node in the tree structure showEditDirectoryDialog(item)
const node = findNodeInTree(treeStructure.value, item.id)
if (node) {
const nodeData = {
id: node.id,
path: node.path,
type: node.type,
sourceFilePath: node.sourceFilePath, // For files
name: node.name
}
if (node.type === 'directory') {
showEditDirectoryDialog(nodeData)
} else {
showEditFileDialog(nodeData)
}
} else { } else {
snackbar.error('无法找到对应的节点') showEditFileDialog(item)
} }
} }
@@ -585,25 +474,6 @@ const findNodeInTree = (nodes: TreeNode[], nodeId: string): TreeNode | null => {
return null return null
} }
// 通过节点名称查找节点的辅助函数
const findNodeInTreeByName = (
nodes: TreeNode[],
nodeName: string
): TreeNode | null => {
for (const node of nodes) {
if (node.name === nodeName) {
return node
}
if (node.children) {
const found = findNodeInTreeByName(node.children, nodeName)
if (found) {
return found
}
}
}
return null
}
// Show dialog to edit directory // Show dialog to edit directory
const showEditDirectoryDialog = (directory: TreeNode) => { const showEditDirectoryDialog = (directory: TreeNode) => {
@@ -621,7 +491,7 @@ const showEditFileDialog = (file: TreeNode) => {
showFileDialog.value = true showFileDialog.value = true
} }
const deleteItem = async (item: TreeItem) => { const deleteItem = async (item: TreeNode) => {
const confirmed = await ConfirmDialogService.confirm({ const confirmed = await ConfirmDialogService.confirm({
title: '删除确认', title: '删除确认',
message: '确定要删除此项吗?', message: '确定要删除此项吗?',
+11
View File
@@ -9,3 +9,14 @@ export async function openExternal(url: string): Promise<void> {
export async function openFile(type: string): Promise<any> { export async function openFile(type: string): Promise<any> {
return window.mainApi.invoke('msgOpenFile', type) return window.mainApi.invoke('msgOpenFile', type)
} }
/**
* 生成唯一ID
* @param prefix ID前缀,默认为空
* @returns 唯一ID字符串
*/
export function generateUniqueId(prefix: string = ''): string {
const timestamp = Date.now().toString(36) // 转换为36进制,更短
const randomStr = Math.random().toString(36).substring(2, 9) // 7位随机字符
return prefix ? `${prefix}-${timestamp}-${randomStr}` : `${timestamp}-${randomStr}`
}