feat(模板): 添加模板复制功能并优化文件操作

- 在模板列表和创建模板界面添加复制模板功能
- 将模板卡片样式改为outlined并修正节点统计标签
- 将同步文件操作改为异步操作提升性能
- 在执行模板对话框显示当前模板名称
This commit is contained in:
zhanggy committed 2025-10-31 18:42:31 +08:00
1 parent 466c02773f
commit 5d7d31c04d
4 files changed
+59 -29

No files matched your search

+25 -26
View File
@@ -2,12 +2,13 @@ import { ipcMain, shell, IpcMainEvent, dialog } from 'electron'
import Constants from './utils/Constants'
import { join, dirname } from 'path'
import {
writeFileSync,
readFileSync,
existsSync,
ensureDirSync,
copyFileSync,
readdirSync
writeFile,
readFile,
pathExists,
ensureDir,
copy,
readdir,
stat,
} from 'fs-extra'
import { autoUpdater } from 'electron-updater'
import { Template, TreeNode } from '../types'
@@ -95,21 +96,19 @@ export default class IPCs {
'msgFindFilesInFolder',
async (event: IpcMainEvent, folderPath: string) => {
try {
const { readdirSync, statSync } = require('fs')
const { join } = require('path')
const foundFiles: string[] = []
const searchFiles = (dir: string) => {
const items = readdirSync(dir)
const searchFiles = async (dir: string) => {
const items = await readdir(dir)
for (const item of items) {
const fullPath = join(dir, item)
const stats = statSync(fullPath)
const stats = await stat(fullPath)
if (stats.isDirectory()) {
// Recursively search in subdirectories
searchFiles(fullPath)
await searchFiles(fullPath)
} else if (stats.isFile()) {
// Add file to the list
foundFiles.push(fullPath)
@@ -117,7 +116,7 @@ export default class IPCs {
}
}
searchFiles(folderPath)
await searchFiles(folderPath)
return foundFiles
} catch (error) {
@@ -136,10 +135,10 @@ export default class IPCs {
const filePath = join(userDataPath, fileName)
// Ensure directory exists
ensureDirSync(userDataPath)
await ensureDir(userDataPath)
if (existsSync(filePath)) {
const data = readFileSync(filePath, 'utf-8')
if (await pathExists(filePath)) {
const data = await readFile(filePath, 'utf-8')
return data
}
return null
@@ -159,9 +158,9 @@ export default class IPCs {
const filePath = join(userDataPath, fileName)
// Ensure directory exists
ensureDirSync(userDataPath)
await ensureDir(userDataPath)
writeFileSync(filePath, data, 'utf-8')
await writeFile(filePath, data, 'utf-8')
return true
} catch (error) {
console.error('Error saving data:', error)
@@ -185,8 +184,8 @@ export default class IPCs {
const filePath = join(userDataPath, 'templates.json')
let templates: Template[] = []
if (existsSync(filePath)) {
const templatesData = readFileSync(filePath, 'utf-8')
if (await pathExists(filePath)) {
const templatesData = await readFile(filePath, 'utf-8')
templates = JSON.parse(templatesData)
}
@@ -202,7 +201,7 @@ export default class IPCs {
// Create base directory
const baseDirPath = join(targetPath, baseDirName)
ensureDirSync(baseDirPath)
await ensureDir(baseDirPath)
// Helper function to traverse the tree and collect all nodes
const collectAllNodes = (nodes: TreeNode[], allNodes: TreeNode[] = [], parent?: TreeNode) => {
@@ -227,20 +226,20 @@ export default class IPCs {
const nodePath = join(baseDirPath, node.path);
if (node.type === 'directory') {
ensureDirSync(nodePath);
await ensureDir(nodePath);
} else if (node.type === 'file') {
const fileDir = dirname(nodePath);
ensureDirSync(fileDir);
await ensureDir(fileDir);
if (node.sourceType === 'file') {
copyFileSync(node.sourcePath, nodePath);
await copy(node.sourcePath, nodePath);
} else {
// Write content directly
const files = readdirSync(node.sourcePath, { withFileTypes: true })
const files = await readdir(node.sourcePath, { withFileTypes: true })
for (const file of files) {
if (file.isFile() && file.name.startsWith(baseDirName)) {
const filePath = join(node.sourcePath, file.name);
copyFileSync(filePath, nodePath);
await copy(filePath, nodePath);
}
}
}
+10 -2
View File
@@ -1,5 +1,5 @@
<template>
<v-card class="d-flex flex-column" height="100%">
<v-card class="d-flex flex-column" height="100%" variant="outlined">
<v-card-item>
<v-card-title>{{ template.name }}</v-card-title>
<v-card-subtitle>{{ template.description }}</v-card-subtitle>
@@ -8,7 +8,7 @@
<v-card-text class="flex-grow-1 py-2">
<v-chip-group column>
<v-chip size="small" color="primary" variant="tonal">
节点: {{ nodeCount.files }}
文件: {{ nodeCount.files }}
</v-chip>
<v-chip size="small" color="success" variant="tonal">
目录: {{ nodeCount.directories }}
@@ -43,6 +43,13 @@
<v-list-item-title>修改</v-list-item-title>
</v-list-item>
<v-list-item @click="$emit('copy', template)">
<template v-slot:prepend>
<v-icon color="primary">mdi-content-copy</v-icon>
</template>
<v-list-item-title>复制</v-list-item-title>
</v-list-item>
<v-divider></v-divider>
<v-list-item @click="$emit('delete', template)">
@@ -70,6 +77,7 @@ interface Emits {
(e: 'execute', template: Template): void
(e: 'edit', template: Template): void
(e: 'delete', template: Template): void
(e: 'copy', template: Template): void
}
const props = defineProps<Props>()
@@ -163,6 +163,9 @@ const snackbar = useSnackbarStore()
// Extract methods from store
const { getTemplateById } = templateStore
// Check if we're copying a template
const isCopyingTemplate = computed(() => !route.params.id && !!route.query.copyId)
// Form state
const formValid = ref(false)
const templateName = ref('')
@@ -218,6 +221,20 @@ onMounted(async () => {
} else {
router.push('/templates')
}
} else if (isCopyingTemplate.value) {
// Handle copy mode
const copyId = route.query.copyId as string
if (copyId) {
const template = getTemplateById(copyId)
if (template) {
templateName.value = `${template.name} - 副本`
templateDescription.value = template.description
treeStructure.value = [...template.treeStructure]
} else {
snackbar.error('找不到要复制的模板')
router.push('/templates')
}
}
}
+7 -1
View File
@@ -33,6 +33,7 @@
@view="viewTemplate"
@execute="executeTemplate"
@edit="editTemplate"
@copy="copyTemplate"
@delete="deleteTemplate"
/>
</v-col>
@@ -64,7 +65,7 @@
<!-- Execute template dialog -->
<v-dialog v-model="showExecuteDialog" max-width="600">
<v-card>
<v-card-title>执行模板</v-card-title>
<v-card-title>执行模板: {{ selectedTemplate?.name }}</v-card-title>
<v-card-text>
<v-form v-model="executeFormValid">
<v-text-field v-model="executePath" label="选择目录" readonly @click="selectDirectory"
@@ -140,6 +141,11 @@ const editTemplate = (template: Template) => {
router.push(`/create-template/${template.id}`)
}
const copyTemplate = (template: Template) => {
router.push(`/create-template?copyId=${template.id}`)
}
const deleteTemplate = async (template: Template) => {
const confirmed = await ConfirmDialogService.confirm({
title: '删除模板',