refactor(模板创建): 重构模板树结构处理逻辑
- 将 parentDirectory 类型从 string 改为 TreeNode 以直接引用节点 - 移除冗余的 TreeItem 接口和转换逻辑 - 简化文件/目录添加和更新逻辑,直接操作 TreeNode - 使用新的 generateUniqueId 工具函数生成唯一ID - 移除不必要的路径拼接和重复检查逻辑
This commit is contained in:
1 parent
d3f4242445
commit
47fb017e21
4 files changed
+78
-207
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
@@ -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) {
|
||||
// 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
|
||||
treeStructure.value = addChildToNode(
|
||||
treeStructure.value,
|
||||
parentDirectoryForNewItems.value,
|
||||
newNode
|
||||
)
|
||||
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,65 +405,29 @@ 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
|
||||
const newFileNode: TreeNode = {
|
||||
id: newId,
|
||||
name: path,
|
||||
path: path,
|
||||
type: 'file',
|
||||
sourceFilePath
|
||||
}
|
||||
|
||||
// If we have a selected file, just add it
|
||||
if (sourceFilePath) {
|
||||
const newFileNode: TreeNode = {
|
||||
id: newId,
|
||||
name: path,
|
||||
path: fullPath,
|
||||
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)
|
||||
}
|
||||
// Add to tree structure
|
||||
if (parentDirectoryForNewItems.value) {
|
||||
// Add as child to the parent directory
|
||||
const parentNode = parentDirectoryForNewItems.value
|
||||
parentNode.children = parentNode.children || []
|
||||
parentNode.children.push(newFileNode)
|
||||
} else {
|
||||
// Otherwise, show warning
|
||||
snackbar.warning('请先选择一个文件或文件夹')
|
||||
return
|
||||
// Add as root node
|
||||
treeStructure.value.push(newFileNode)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
showEditFileDialog(nodeData)
|
||||
}
|
||||
const editItem = (item: TreeNode) => {
|
||||
if (item.type === 'directory') {
|
||||
showEditDirectoryDialog(item)
|
||||
} 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: '确定要删除此项吗?',
|
||||
|
||||
@@ -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}`
|
||||
}
|
||||
Reference in new issue
Block a user