feat: 添加 TypeScript 类型检查命令,优化模板创建流程,移除不必要的上下文菜单逻辑

This commit is contained in:
zhanggy committed 2025-10-28 17:26:25 +08:00
1 parent 562e4f5b9e
commit 157bc47f3c
4 files changed
+70 -144

No files matched your search

+23 -63
View File
@@ -3,7 +3,6 @@
variant="outlined"
class="pa-2"
:height="height"
@contextmenu.prevent="emit('showContextMenu', $event, null)"
>
<v-treeview
v-model:expanded="localExpanded"
@@ -31,27 +30,9 @@
<template #title="{ item }">
<span
@contextmenu.prevent="emit('showContextMenu', $event, item)"
class="cursor-pointer d-flex align-center"
>
{{ item.name }}
<v-btn
v-if="!readOnly && item.type === 'directory'"
icon
variant="text"
size="small"
class="ml-2"
@click.stop="emit('addNewItemToCurrentDirectory', item)"
>
<v-icon size="small">mdi-plus</v-icon>
<v-tooltip
v-if="!readOnly"
activator="parent"
location="top"
>
添加到目录
</v-tooltip>
</v-btn>
</span>
</template>
@@ -61,23 +42,24 @@
class="d-flex align-center ga-1"
>
<v-btn
v-if="!readOnly"
v-if="!readOnly && item.type === 'directory'"
icon
variant="text"
size="small"
@click.stop="emit('editItem', item)"
class="ml-2"
@click.stop="emit('addNewItemToCurrentDirectory', item)"
>
<v-icon size="small"> mdi-pencil </v-icon>
<v-icon size="small">mdi-folder-plus</v-icon>
<v-tooltip
activator="parent"
location="top"
>
{{ item.type === 'file' ? '编辑文件' : '编辑目录' }}
添加到目录
</v-tooltip>
</v-btn>
<v-btn
v-if="item.type === 'directory' && !readOnly"
v-if="item.type === 'directory'"
icon
variant="text"
size="small"
@@ -92,6 +74,21 @@
</v-tooltip>
</v-btn>
<v-btn
icon
variant="text"
size="small"
@click.stop="emit('editItem', item)"
>
<v-icon size="small"> mdi-pencil </v-icon>
<v-tooltip
activator="parent"
location="top"
>
{{ item.type === 'file' ? '编辑文件' : '编辑目录' }}
</v-tooltip>
</v-btn>
<v-btn
icon
variant="text"
@@ -99,7 +96,6 @@
@click.stop="emit('deleteItem', item)"
>
<v-icon
v-if="!readOnly"
size="small"
color="error"
>
@@ -133,39 +129,12 @@
v-if="!readOnly"
class="text-caption"
>
右键点击空白区域添加根目录或文件
点击上方按钮添加根目录或文件
</p>
</div>
</v-card>
<!-- Context Menu -->
<v-menu
v-if="!readOnly"
v-model="localShowContextMenuVar"
:position-x="contextMenuX"
:position-y="contextMenuY"
absolute
>
<v-list density="compact">
<v-list-item @click="emit('addDirectory', currentContextItem)">
<template #prepend>
<v-icon>mdi-folder-plus</v-icon>
</template>
<v-list-item-title>
{{ currentContextItem ? '添加子目录' : '添加根目录' }}
</v-list-item-title>
</v-list-item>
<v-list-item @click="emit('addFile', currentContextItem)">
<template #prepend>
<v-icon>mdi-file-plus</v-icon>
</template>
<v-list-item-title>
{{ currentContextItem ? '添加子文件' : '添加根文件' }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
<script setup lang="ts">
@@ -182,18 +151,12 @@ interface Props {
height?: number
readOnly?: boolean
emptyText?: string
contextMenuX?: number
contextMenuY?: number
currentContextItem?: TreeItem | null
}
const props = withDefaults(defineProps<Props>(), {
height: 600,
readOnly: false,
emptyText: '暂无文件或目录',
contextMenuX: 0,
contextMenuY: 0,
currentContextItem: null
emptyText: '暂无文件或目录'
})
const localExpanded = defineModel<string[]>('expanded', {
@@ -205,13 +168,10 @@ const localShowContextMenuVar = defineModel<boolean>('showContextMenuVar', {
})
const emit = defineEmits([
'showContextMenu',
'addNewItemToCurrentDirectory',
'editFile',
'editItem',
'deleteItem',
'addDirectory',
'addFile',
'addFileToDirectory'
])
</script>
+41 -74
View File
@@ -142,19 +142,12 @@
:items="treeItems"
:height="treeHeight"
v-model:expanded="expandedNodes"
v-model:showContextMenuVar="showContextMenuVar"
:context-menu-x="contextMenuX"
:context-menu-y="contextMenuY"
:current-context-item="currentContextItem"
:empty-text="'暂无文件或目录'"
:read-only="false"
@showContextMenu="showContextMenu"
@add-new-item-to-current-directory="addNewItemToCurrentDirectory"
@edit-file="editFile"
@edit-item="editItem"
@delete-item="deleteItem"
@add-directory="addDirectory"
@add-file="addFile"
@add-file-to-directory="addFileToDirectory"
/>
</v-col>
@@ -183,10 +176,10 @@
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
<v-btn
@click="closeDirectoryDialog"
>
取消
>
取消
</v-btn>
<v-btn
color="primary"
@@ -218,7 +211,7 @@
ref="fileInputRef"
@keydown.enter="selectAndAddFile"
/>
<div class="d-flex align-center my-4">
<v-btn
@click="selectSourceFile"
@@ -237,7 +230,7 @@
选择文件夹
</v-btn>
</div>
<!-- Display selected source -->
<div v-if="selectedSourcePath" class="d-flex align-center">
<v-icon :color="isAutoFindMode ? 'warning' : 'info'" class="mr-2">
@@ -245,7 +238,7 @@
</v-icon>
<span class="text-truncate">{{ selectedSourcePath }}</span>
</div>
<!-- Auto-find indicator -->
<v-alert
v-if="isAutoFindMode"
@@ -258,7 +251,7 @@
<span>自动寻找文件夹内的文件</span>
</div>
</v-alert>
<!-- Note when editing -->
<v-alert
v-if="currentEditingItem"
@@ -275,10 +268,10 @@
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
<v-btn
@click="closeFileDialog"
>
取消
>
取消
</v-btn>
<v-btn
color="primary"
@@ -384,11 +377,7 @@ const fileFormValid = ref(false)
const directoryInputRef = ref<HTMLInputElement | null>(null)
const fileInputRef = ref<HTMLInputElement | null>(null)
// Context menu
const showContextMenuVar = ref(false)
const contextMenuX = ref(0)
const contextMenuY = ref(0)
const currentContextItem = ref<TreeItem | null>(null)
// Snackbar variables
const snackbarVisible = ref(false)
@@ -440,10 +429,10 @@ const treeItems = computed<TreeItem[]>(() => {
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i]
const isLastPart = i === pathParts.length - 1
// Check if this part already exists in the current level
let existingItem = currentLevel.find(item => item.name === part)
if (!existingItem) {
// Create new directory item
const newDir: TreeItem = {
@@ -455,7 +444,7 @@ const treeItems = computed<TreeItem[]>(() => {
currentLevel.push(newDir)
existingItem = newDir
}
// Move to the next level if not the last part
if (!isLastPart && existingItem.children) {
currentLevel = existingItem.children
@@ -467,16 +456,16 @@ const treeItems = computed<TreeItem[]>(() => {
files.value.forEach((file) => {
const pathParts = file.path.split('/').filter(part => part !== '')
const fileName = pathParts.pop() // Get the last part as filename
if (!fileName) return // Skip if no filename
let currentLevel = root.children || []
// Navigate to the appropriate directory for the file
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i]
const dirItem = currentLevel.find(item => item.name === part && item.type === 'directory')
if (dirItem && dirItem.children) {
currentLevel = dirItem.children
} else {
@@ -491,7 +480,7 @@ const treeItems = computed<TreeItem[]>(() => {
currentLevel = newDir.children || []
}
}
// Add the file to the current level
currentLevel.push({
id: `file-${fileName}-${Date.now()}-${Math.random()}`,
@@ -554,18 +543,7 @@ const updateTreeHeight = () => {
treeHeight.value = window.innerHeight - 200
}
const showContextMenu = (event: MouseEvent, item: TreeItem | null) => {
event.preventDefault()
contextMenuX.value = event.clientX
contextMenuY.value = event.clientY
currentContextItem.value = item
showContextMenuVar.value = false // Close first to reset position
nextTick(() => {
contextMenuX.value = event.clientX
contextMenuY.value = event.clientY
showContextMenuVar.value = true
})
}
const addRootDirectory = () => {
newDirectoryPath.value = ''
@@ -580,8 +558,6 @@ const addRootFile = () => {
}
const addDirectory = (parentItem: TreeItem | null = null) => {
showContextMenuVar.value = false
currentContextItem.value = parentItem
parentDirectoryForNewItems.value = parentItem ? parentItem.name : null // Store parent context
newDirectoryPath.value = '' // Don't pre-fill the path
showDirectoryDialog.value = true
@@ -589,7 +565,6 @@ const addDirectory = (parentItem: TreeItem | null = null) => {
}
const addFile = (parentItem: TreeItem | null = null) => {
showContextMenuVar.value = false
parentDirectoryForNewItems.value = parentItem ? parentItem.name : null // Store parent context
newFilePath.value = '' // Don't pre-fill the path
showFileDialog.value = true
@@ -598,7 +573,6 @@ const addFile = (parentItem: TreeItem | null = null) => {
// Add a file directly to a specific directory
const addFileToDirectory = (directoryItem: TreeItem) => {
showContextMenuVar.value = false
parentDirectoryForNewItems.value = directoryItem.name // Store the directory as parent
newFilePath.value = '' // Don't pre-fill the path
showFileDialog.value = true
@@ -606,7 +580,6 @@ const addFileToDirectory = (directoryItem: TreeItem) => {
}
const addNewItemToCurrentDirectory = (directoryItem: TreeItem) => {
currentContextItem.value = directoryItem
parentDirectoryForNewItems.value = directoryItem.name // Store parent context
newDirectoryPath.value = '' // Don't pre-fill the path
showDirectoryDialog.value = true
@@ -620,8 +593,8 @@ const confirmAddDirectory = () => {
let fullPath = newDirectoryPath.value
if (parentDirectoryForNewItems.value) {
// Make sure parent directory ends with a slash before appending
const parentPath = parentDirectoryForNewItems.value.endsWith('/')
? parentDirectoryForNewItems.value
const parentPath = parentDirectoryForNewItems.value.endsWith('/')
? parentDirectoryForNewItems.value
: parentDirectoryForNewItems.value + '/'
fullPath = parentPath + newDirectoryPath.value
}
@@ -632,7 +605,7 @@ const confirmAddDirectory = () => {
if (index !== -1) {
// Update the existing directory path
directories.value[index].path = fullPath
// If the directory path changes, update any files/dirs that were inside it
updatePathsForRenamedDirectory(currentEditingItem.value.path, fullPath)
}
@@ -654,9 +627,8 @@ const confirmAddDirectory = () => {
showDirectoryDialog.value = false
newDirectoryPath.value = ''
parentDirectoryForNewItems.value = null // Clear parent context after creation
currentContextItem.value = null
currentEditingItem.value = null
// If we added to a parent directory, make sure it's expanded
if (parentDirectoryForNewItems.value) {
if (!expandedNodes.value.includes(`dir-${parentDirectoryForNewItems.value}`)) {
@@ -677,7 +649,7 @@ const updatePathsForRenamedDirectory = (oldPath: string, newPath: string) => {
}
return file
})
// Update all directories that were under the old directory
directories.value = directories.value.map(dir => {
if (dir.path.startsWith(oldPath + '/') || dir.path === oldPath) {
@@ -695,7 +667,6 @@ const closeDirectoryDialog = () => {
showDirectoryDialog.value = false
newDirectoryPath.value = ''
parentDirectoryForNewItems.value = null
currentContextItem.value = null
currentEditingItem.value = null
}
@@ -703,7 +674,6 @@ const closeFileDialog = () => {
showFileDialog.value = false
newFilePath.value = ''
parentDirectoryForNewItems.value = null
currentContextItem.value = null
currentEditingItem.value = null
selectedSourcePath.value = ''
isAutoFindMode.value = false
@@ -716,8 +686,8 @@ const selectAndAddFile = async () => {
let fullPath = newFilePath.value
if (parentDirectoryForNewItems.value) {
// Make sure parent directory ends with a slash before appending
const parentPath = parentDirectoryForNewItems.value.endsWith('/')
? parentDirectoryForNewItems.value
const parentPath = parentDirectoryForNewItems.value.endsWith('/')
? parentDirectoryForNewItems.value
: parentDirectoryForNewItems.value + '/'
fullPath = parentPath + newFilePath.value
}
@@ -744,13 +714,13 @@ const selectAndAddFile = async () => {
if (isAutoFindMode.value && selectedSourcePath.value) {
// Auto-find files in the selected folder
const foundFiles = await window.mainApi.invoke('msgFindFilesInFolder', selectedSourcePath.value)
// Add all found files as template files
for (const foundFile of foundFiles) {
// Calculate relative path from selected folder to the found file
const relativePath = foundFile.replace(selectedSourcePath.value, '').substring(1) // Remove leading slash
const fullTargetPath = fullPath ? `${fullPath}/${relativePath}` : relativePath
// Check for duplicates
const duplicate = files.value.some(f => f.path === fullTargetPath)
if (!duplicate) {
@@ -761,19 +731,18 @@ const selectAndAddFile = async () => {
files.value.push(newFile)
}
}
// Reset selection state
selectedSourcePath.value = ''
isAutoFindMode.value = false
showFileDialog.value = false
newFilePath.value = ''
parentDirectoryForNewItems.value = null // Clear parent context after creation
currentContextItem.value = null
currentEditingItem.value = null
return
}
// If we have a selected file, just add it
if (selectedSourcePath.value) {
const newFile = {
@@ -790,11 +759,10 @@ const selectAndAddFile = async () => {
showFileDialog.value = false
newFilePath.value = ''
parentDirectoryForNewItems.value = null // Clear parent context after creation
currentContextItem.value = null
currentEditingItem.value = null
return
}
// Otherwise, show file selection dialog
showSnackbar('请先选择一个文件或文件夹', 'warning')
return
@@ -803,11 +771,10 @@ const selectAndAddFile = async () => {
// Reset selection state after editing
selectedSourcePath.value = ''
isAutoFindMode.value = false
showFileDialog.value = false
newFilePath.value = ''
parentDirectoryForNewItems.value = null
currentContextItem.value = null
currentEditingItem.value = null
}
@@ -820,7 +787,7 @@ const goBack = async () => {
cancelText: '取消',
confirmColor: 'warning'
})
if (confirmed) {
router.push('/templates')
}
@@ -831,7 +798,7 @@ const goBack = async () => {
const expandAll = () => {
const allIds: string[] = []
const collectIds = (items: TreeItem[]) => {
items.forEach(item => {
if (item.children && item.children.length > 0) {
@@ -840,7 +807,7 @@ const expandAll = () => {
}
})
}
collectIds(treeItems.value)
expandedNodes.value = allIds
}
@@ -899,13 +866,13 @@ const showEditFileDialog = (file: { path: string; content?: string; sourceFilePa
// Set up for editing an existing file
currentEditingItem.value = { ...file, type: 'file' }
newFilePath.value = file.path // Set the current path for editing
// If the file has a source file path, set it in the selection state
if (file.sourceFilePath) {
selectedSourcePath.value = file.sourceFilePath
isAutoFindMode.value = false
}
showFileDialog.value = true
autoFocusFileInput()
}
@@ -921,7 +888,7 @@ const deleteItem = async (item: TreeItem) => {
cancelText: '取消',
confirmColor: 'error'
})
if (!confirmed) return
if (item.type === 'directory') {
@@ -931,7 +898,7 @@ const deleteItem = async (item: TreeItem) => {
// Exclude if exact match or it's a subdirectory
return !isSubPath(d.path, item.name);
});
// Also remove files that are inside this directory
files.value = files.value.filter(f => {
// Exclude if it's inside the directory being deleted