feat(模板创建): 改进文件选择功能并优化数据结构
重构文件对话框组件,分离文件和文件夹选择功能 更新TreeNode接口,增加sourceType字段区分来源类型 优化文件选择逻辑,提供更清晰的用户指引
This commit is contained in:
1 parent
47fb017e21
commit
f3e909923a
3 files changed
+74
-25
No files matched your search
@@ -20,17 +20,30 @@
|
|||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 文件路径选择输入框 -->
|
<!-- 文件选择输入框 -->
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="selectedSourcePath"
|
v-model="selectedFilePath"
|
||||||
label="文件或文件夹"
|
label="选择文件"
|
||||||
placeholder="点击输入框选择文件或文件夹"
|
placeholder="点击输入框选择文件"
|
||||||
readonly
|
readonly
|
||||||
:rules="isEditMode ? [] : [requiredRule('文件或文件夹')]"
|
:rules="[fileSelectionRule]"
|
||||||
:append-inner-icon="'mdi-file'"
|
:append-inner-icon="'mdi-file'"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
@click="selectSourceFileOrFolder()"
|
@click="selectSourceFile"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 文件夹选择输入框 -->
|
||||||
|
<v-text-field
|
||||||
|
v-model="selectedFolderPath"
|
||||||
|
label="选择文件夹"
|
||||||
|
placeholder="点击输入框选择文件夹"
|
||||||
|
readonly
|
||||||
|
:rules="[fileSelectionRule]"
|
||||||
|
:append-inner-icon="'mdi-folder'"
|
||||||
|
density="comfortable"
|
||||||
|
class="mt-4"
|
||||||
|
@click="selectSourceFolder"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 选择提示 -->
|
<!-- 选择提示 -->
|
||||||
@@ -44,6 +57,9 @@
|
|||||||
<span>
|
<span>
|
||||||
选择文件夹则会自动匹配该文件夹下的文件进行复制并且重命名
|
选择文件夹则会自动匹配该文件夹下的文件进行复制并且重命名
|
||||||
</span>
|
</span>
|
||||||
|
<span class="mt-1 font-weight-bold">
|
||||||
|
文件和文件夹选择其中一个即可
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</v-alert>
|
</v-alert>
|
||||||
</v-form>
|
</v-form>
|
||||||
@@ -54,7 +70,7 @@
|
|||||||
<v-btn
|
<v-btn
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="confirm"
|
@click="confirm"
|
||||||
:disabled="!formValid || (!selectedSourcePath && !isEditMode)"
|
:disabled="!formValid || (!selectedFilePath && !selectedFolderPath && !isEditMode)"
|
||||||
>
|
>
|
||||||
{{ isEditMode ? '保存' : '添加' }}
|
{{ isEditMode ? '保存' : '添加' }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@@ -76,7 +92,7 @@ interface Props {
|
|||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'update:modelValue', value: boolean): void
|
(e: 'update:modelValue', value: boolean): void
|
||||||
(e: 'confirm', data: { path: string; sourceFilePath?: string }): void
|
(e: 'confirm', data: { path: string; sourcePath?: string; sourceType?: 'file' | 'folder' }): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -91,7 +107,8 @@ const emit = defineEmits<Emits>()
|
|||||||
const isVisible = ref(false)
|
const isVisible = ref(false)
|
||||||
const formValid = ref(false)
|
const formValid = ref(false)
|
||||||
const filePath = ref('')
|
const filePath = ref('')
|
||||||
const selectedSourcePath = ref<string>('')
|
const selectedFilePath = ref<string>('')
|
||||||
|
const selectedFolderPath = ref<string>('')
|
||||||
const inputRef = ref<HTMLInputElement | null>(null)
|
const inputRef = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
@@ -103,7 +120,8 @@ watch(() => props.modelValue, (newValue) => {
|
|||||||
if (newValue) {
|
if (newValue) {
|
||||||
// Reset form when dialog opens
|
// Reset form when dialog opens
|
||||||
filePath.value = props.editingItem?.path || ''
|
filePath.value = props.editingItem?.path || ''
|
||||||
selectedSourcePath.value = props.editingItem?.sourceFilePath || ''
|
selectedFilePath.value = props.editingItem?.sourceType === 'file' ? props.editingItem?.sourcePath || '' : ''
|
||||||
|
selectedFolderPath.value = props.editingItem?.sourceType === 'folder' ? props.editingItem?.sourcePath || '' : ''
|
||||||
autoFocusInput()
|
autoFocusInput()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -117,6 +135,12 @@ watch(isVisible, (newValue) => {
|
|||||||
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
|
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
|
||||||
!!v || `${fieldName}为必填项`
|
!!v || `${fieldName}为必填项`
|
||||||
|
|
||||||
|
// File selection validation rule
|
||||||
|
const fileSelectionRule = () => {
|
||||||
|
if (isEditMode.value) return true // 编辑模式下不需要验证
|
||||||
|
return !!selectedFilePath.value || !!selectedFolderPath.value || '请选择文件或文件夹'
|
||||||
|
}
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const autoFocusInput = async () => {
|
const autoFocusInput = async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
@@ -125,22 +149,45 @@ const autoFocusInput = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectSourceFileOrFolder = async () => {
|
const selectSourceFile = async () => {
|
||||||
// 使用现有的主进程API来选择文件或文件夹
|
// 使用主进程API来选择文件
|
||||||
const result = await window.mainApi.invoke('msgSelectFileOrFolder')
|
const result = await window.mainApi.invoke('msgOpenFile', 'any')
|
||||||
|
|
||||||
if (result && result.filePath) {
|
if (result && !result.canceled && result.filePaths.length > 0) {
|
||||||
selectedSourcePath.value = result.filePath
|
selectedFilePath.value = result.filePaths[0]
|
||||||
|
selectedFolderPath.value = '' // 清空文件夹选择
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectSourceFolder = async () => {
|
||||||
|
// 使用主进程API来选择文件夹
|
||||||
|
const result = await window.mainApi.invoke('msgSelectDirectory')
|
||||||
|
|
||||||
|
if (result && !result.canceled && result.filePath) {
|
||||||
|
selectedFolderPath.value = result.filePath
|
||||||
|
selectedFilePath.value = '' // 清空文件选择
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirm = () => {
|
const confirm = () => {
|
||||||
if (!filePath.value.trim()) return
|
if (!filePath.value.trim()) return
|
||||||
|
|
||||||
|
// 确定选择的源类型和路径
|
||||||
|
let sourcePathValue: string | undefined
|
||||||
|
let sourceType: 'file' | 'folder' | undefined
|
||||||
|
|
||||||
|
if (selectedFilePath.value) {
|
||||||
|
sourcePathValue = selectedFilePath.value
|
||||||
|
sourceType = 'file'
|
||||||
|
} else if (selectedFolderPath.value) {
|
||||||
|
sourcePathValue = selectedFolderPath.value
|
||||||
|
sourceType = 'folder'
|
||||||
|
}
|
||||||
|
|
||||||
emit('confirm', {
|
emit('confirm', {
|
||||||
path: filePath.value,
|
path: filePath.value,
|
||||||
sourceFilePath: selectedSourcePath.value || undefined
|
sourcePath: sourcePathValue,
|
||||||
|
sourceType
|
||||||
})
|
})
|
||||||
|
|
||||||
close()
|
close()
|
||||||
@@ -148,7 +195,8 @@ const confirm = () => {
|
|||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
filePath.value = ''
|
filePath.value = ''
|
||||||
selectedSourcePath.value = ''
|
selectedFilePath.value = ''
|
||||||
|
selectedFolderPath.value = ''
|
||||||
formValid.value = false
|
formValid.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,13 +284,11 @@ const handleDirectoryDialogConfirm = (data: { path: string }) => {
|
|||||||
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.id,
|
||||||
updatedNode
|
updatedNode
|
||||||
)
|
)
|
||||||
console.log('update treeStructure', treeStructure.value)
|
|
||||||
} else {
|
} else {
|
||||||
// Create new directory node
|
// Create new directory node
|
||||||
const newNode: TreeNode = {
|
const newNode: TreeNode = {
|
||||||
@@ -360,8 +358,8 @@ const updateNodeInTree = (
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleFileDialogConfirm = (data: { path: string; sourceFilePath?: string }) => {
|
const handleFileDialogConfirm = (data: { path: string; sourcePath?: string; sourceType?: 'file' | 'folder' }) => {
|
||||||
const { path, sourceFilePath } = data
|
const { path, sourcePath, sourceType } = data
|
||||||
|
|
||||||
const newId = generateUniqueId('file')
|
const newId = generateUniqueId('file')
|
||||||
|
|
||||||
@@ -373,7 +371,8 @@ const handleFileDialogConfirm = (data: { path: string; sourceFilePath?: string }
|
|||||||
name: path,
|
name: path,
|
||||||
path: path,
|
path: path,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
sourceFilePath: sourceFilePath
|
sourcePath: sourcePath,
|
||||||
|
sourceType
|
||||||
}
|
}
|
||||||
treeStructure.value = updateNodeInTree(
|
treeStructure.value = updateNodeInTree(
|
||||||
treeStructure.value,
|
treeStructure.value,
|
||||||
@@ -410,7 +409,8 @@ const handleFileDialogConfirm = (data: { path: string; sourceFilePath?: string }
|
|||||||
name: path,
|
name: path,
|
||||||
path: path,
|
path: path,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
sourceFilePath
|
sourcePath,
|
||||||
|
sourceType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to tree structure
|
// Add to tree structure
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ export interface TreeNode {
|
|||||||
path: string // 完整路径
|
path: string // 完整路径
|
||||||
type: 'directory' | 'file'
|
type: 'directory' | 'file'
|
||||||
children?: TreeNode[]
|
children?: TreeNode[]
|
||||||
sourceFilePath?: string
|
sourcePath?: string
|
||||||
|
sourceType?: 'file' | 'folder' // 记录选择的是文件还是文件夹
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Template {
|
export interface Template {
|
||||||
|
|||||||
Reference in new issue
Block a user