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
+68 -197

No files matched your search

+1 -1
View File
@@ -44,7 +44,7 @@ import { TreeNode } from '@/renderer/store/template'
interface Props {
modelValue: boolean
isEditMode?: boolean
parentDirectory?: string | null
parentDirectory?: TreeNode | null
editingItem?: TreeNode | null
}
+2 -12
View File
@@ -70,13 +70,13 @@ import { TreeNode } from '@/renderer/store/template'
interface Props {
modelValue: boolean
isEditMode?: boolean
parentDirectory?: string | null
parentDirectory?: TreeNode | null
editingItem?: TreeNode | null
}
interface Emits {
(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>(), {
@@ -137,19 +137,9 @@ const selectSourceFileOrFolder = async () => {
const confirm = () => {
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', {
path: filePath.value,
fullPath,
sourceFilePath: selectedSourcePath.value || undefined
})
+54 -184
View File
@@ -111,7 +111,7 @@
</div>
<TemplateTree
:items="treeItems"
:items="treeStructure"
:height="treeHeight"
v-model:expanded="expandedNodes"
:empty-text="'暂无文件或目录'"
@@ -152,6 +152,7 @@ import TemplateTree from '@/renderer/components/TemplateTree.vue'
import DirectoryDialog from '@/renderer/components/DirectoryDialog.vue'
import FileDialog from '@/renderer/components/FileDialog.vue'
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
import { generateUniqueId } from '@/renderer/utils'
const router = useRouter()
const route = useRoute()
@@ -172,7 +173,7 @@ const showFileDialog = ref(false)
// Directory dialog state
const showDirectoryDialog = ref(false)
const parentDirectoryForNewItems = ref<string | null>(null)
const parentDirectoryForNewItems = 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 treeHeight = ref(600)
// Define type for tree items
interface TreeItem {
id: string
name: string
type: 'directory' | 'file'
children?: TreeItem[]
}
// Item state - 使用树状结构
const treeStructure = ref<TreeNode[]>([])
// Computed
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
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
@@ -278,49 +256,21 @@ const addRootFile = () => {
// Add a file directly to a specific directory
const addFileToDirectory = (directoryItem: TreeItem) => {
parentDirectoryForNewItems.value = directoryItem.name // Store the directory as parent
const addFileToDirectory = (directoryItem: TreeNode) => {
parentDirectoryForNewItems.value = directoryItem // Store the directory node as parent
currentEditingItem.value = null
showFileDialog.value = true
}
const addNewItemToCurrentDirectory = (directoryItem: TreeItem) => {
parentDirectoryForNewItems.value = directoryItem.name // Store parent context
const addNewItemToCurrentDirectory = (directoryItem: TreeNode) => {
parentDirectoryForNewItems.value = directoryItem // Store the directory node as parent
currentEditingItem.value = null
showDirectoryDialog.value = true
}
const handleDirectoryDialogConfirm = (data: { path: string }) => {
const { path } = data
// 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()}`
const newId = generateUniqueId('dir')
// Check if we're editing an existing directory
if (
@@ -329,16 +279,18 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
) {
// Find and update the existing directory in the tree
const updatedNode: TreeNode = {
id: currentEditingItem.value.id,
...currentEditingItem.value,
name: path,
path: path,
type: 'directory'
}
console.log('updatedNode', updatedNode)
treeStructure.value = updateNodeInTree(
treeStructure.value,
currentEditingItem.value.path,
updatedNode
)
console.log('update treeStructure', treeStructure.value)
} else {
// Create new directory node
const newNode: TreeNode = {
@@ -350,13 +302,29 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
// Add to tree structure
if (parentDirectoryForNewItems.value) {
// Add as child to the parent directory
treeStructure.value = addChildToNode(
treeStructure.value,
parentDirectoryForNewItems.value,
newNode
// 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
parentNode.children = parentNode.children || []
parentNode.children.push(newNode)
} 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
treeStructure.value.push(newNode)
}
@@ -372,45 +340,17 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
// 在树状结构中更新节点的辅助函数
const updateNodeInTree = (
nodes: TreeNode[],
oldPath: string,
oldId: string,
updatedNode: TreeNode
): TreeNode[] => {
return nodes.map((node) => {
if (node.path === oldPath) {
if (node.id === oldId) {
return updatedNode
}
if (node.children) {
return {
...node,
children: updateNodeInTree(node.children, oldPath, 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)
children: updateNodeInTree(node.children, oldId, updatedNode)
}
}
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
if (currentEditingItem.value && currentEditingItem.value.type === 'file') {
@@ -429,24 +371,21 @@ const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceF
const updatedNode: TreeNode = {
id: currentEditingItem.value.id,
name: path,
path: fullPath,
path: path,
type: 'file',
sourceFilePath: sourceFilePath
}
treeStructure.value = updateNodeInTree(
treeStructure.value,
currentEditingItem.value.path,
currentEditingItem.value.id,
updatedNode
)
} else {
// 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 parentNode = parentDirectoryForNewItems.value
if (parentNode.children) {
const duplicateExists = parentNode.children.some(
(child) => child.name === path
)
@@ -466,19 +405,10 @@ const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceF
}
}
// Also check for duplicates using the full path (for backward compatibility)
const hasDuplicate = checkForDuplicateInTree(treeStructure.value, fullPath)
if (hasDuplicate) {
snackbar.error('该文件路径已存在!')
return
}
// If we have a selected file, just add it
if (sourceFilePath) {
const newFileNode: TreeNode = {
id: newId,
name: path,
path: fullPath,
path: path,
type: 'file',
sourceFilePath
}
@@ -486,45 +416,18 @@ const handleFileDialogConfirm = (data: { path: string; fullPath: string; sourceF
// Add to tree structure
if (parentDirectoryForNewItems.value) {
// Add as child to the parent directory
treeStructure.value = addChildToNode(
treeStructure.value,
parentDirectoryForNewItems.value,
newFileNode
)
const parentNode = parentDirectoryForNewItems.value
parentNode.children = parentNode.children || []
parentNode.children.push(newFileNode)
} else {
// Add as root node
treeStructure.value.push(newFileNode)
}
} else {
// Otherwise, show warning
snackbar.warning('请先选择一个文件或文件夹')
return
}
}
showFileDialog.value = false
parentDirectoryForNewItems.value = null // Clear parent context after creation
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 () => {
@@ -547,25 +450,11 @@ const goBack = async () => {
// 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: TreeItem) => {
// Find the node in the tree structure
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)
const editItem = (item: TreeNode) => {
if (item.type === 'directory') {
showEditDirectoryDialog(item)
} else {
showEditFileDialog(nodeData)
}
} else {
snackbar.error('无法找到对应的节点')
showEditFileDialog(item)
}
}
@@ -585,25 +474,6 @@ const findNodeInTree = (nodes: TreeNode[], nodeId: string): TreeNode | 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
const showEditDirectoryDialog = (directory: TreeNode) => {
@@ -621,7 +491,7 @@ const showEditFileDialog = (file: TreeNode) => {
showFileDialog.value = true
}
const deleteItem = async (item: TreeItem) => {
const deleteItem = async (item: TreeNode) => {
const confirmed = await ConfirmDialogService.confirm({
title: '删除确认',
message: '确定要删除此项吗?',
+11
View File
@@ -9,3 +9,14 @@ export async function openExternal(url: string): Promise<void> {
export async function openFile(type: string): Promise<any> {
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}`
}