feat: Implement template management feature
- Added IPC handlers for selecting directories, loading, saving, and executing templates. - Created a new TemplateListScreen for displaying and managing templates. - Developed CreateTemplateScreen for creating and editing templates with file and directory management. - Updated routing to include new template screens. - Enhanced MainScreen with navigation to template management. - Added localization for new template-related strings in English and Chinese. - Introduced a Pinia store for managing template data. - Implemented tests for template creation, listing, and detail viewing.
This commit is contained in:
1 parent
742eb7040f
commit
1aecd5f4b6
15 files changed
+1225
-140
No files matched your search
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<h2 class="mb-4"> 模板列表 </h2>
|
||||
|
||||
<!-- No templates message -->
|
||||
<v-card
|
||||
v-if="templates.length === 0"
|
||||
class="pa-6 mb-4 text-center"
|
||||
>
|
||||
<v-card-title class="text-h5 mb-4">
|
||||
暂无模板
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p>还没有创建任何模板。创建您的第一个模板以开始批量生成文件和目录。</p>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
color="primary"
|
||||
size="x-large"
|
||||
@click="navigateToCreateTemplate"
|
||||
prepend-icon="mdi-plus"
|
||||
>
|
||||
创建模板
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
<!-- Template list -->
|
||||
<div v-else>
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="template in templates"
|
||||
:key="template.id"
|
||||
cols="12"
|
||||
md="6"
|
||||
lg="4"
|
||||
>
|
||||
<v-card
|
||||
class="d-flex flex-column"
|
||||
height="100%"
|
||||
>
|
||||
<v-card-title>{{ template.name }}</v-card-title>
|
||||
<v-card-subtitle>{{ template.description }}</v-card-subtitle>
|
||||
<v-card-text class="flex-grow-1">
|
||||
<p>文件数量: {{ template.files.length }}</p>
|
||||
<p>目录数量: {{ template.directories.length }}</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
@click="viewTemplate(template)"
|
||||
>
|
||||
查看
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="success"
|
||||
variant="outlined"
|
||||
@click="executeTemplate(template)"
|
||||
>
|
||||
执行
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Template detail dialog -->
|
||||
<v-dialog
|
||||
v-model="showTemplateDetail"
|
||||
max-width="800"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span>模板详情: {{ selectedTemplate?.name }}</span>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon
|
||||
@click="showTemplateDetail = false"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-treeview
|
||||
:items="templateTree"
|
||||
item-title="name"
|
||||
item-key="id"
|
||||
item-children="children"
|
||||
activatable
|
||||
open-all
|
||||
>
|
||||
<template v-slot:prepend="{ item, open }">
|
||||
<v-icon>
|
||||
{{
|
||||
item.type === 'directory'
|
||||
? open
|
||||
? 'mdi-folder-open'
|
||||
: 'mdi-folder'
|
||||
: 'mdi-file'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Execute template dialog -->
|
||||
<v-dialog
|
||||
v-model="showExecuteDialog"
|
||||
max-width="600"
|
||||
>
|
||||
<v-card>
|
||||
<v-card-title>执行模板</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="executeFormValid">
|
||||
<v-text-field
|
||||
v-model="executePath"
|
||||
label="选择目录"
|
||||
readonly
|
||||
@click="selectDirectory"
|
||||
prepend-inner-icon="mdi-folder"
|
||||
:rules="[(v) => !!v || '请选择目录']"
|
||||
/>
|
||||
<v-textarea
|
||||
v-model="baseDirNames"
|
||||
label="基础目录名 (每行一个,支持批量)"
|
||||
placeholder="输入基础目录名,每行一个"
|
||||
:rules="[(v) => !!v || '请输入基础目录名']"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showExecuteDialog = false"> 取消 </v-btn>
|
||||
<v-btn
|
||||
color="success"
|
||||
@click="confirmExecuteTemplate"
|
||||
:disabled="!executeFormValid"
|
||||
>
|
||||
确认执行
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Floating Action Button for creating templates -->
|
||||
<v-btn
|
||||
v-if="templates.length > 0"
|
||||
color="primary"
|
||||
size="x-large"
|
||||
icon="mdi-plus"
|
||||
fixed
|
||||
location="bottom end"
|
||||
class="mb-8 mr-8"
|
||||
style="z-index: 1000;"
|
||||
@click="navigateToCreateTemplate"
|
||||
>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
>
|
||||
创建模板
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useTemplateStore } from '@/renderer/store/template'
|
||||
|
||||
const router = useRouter()
|
||||
const templateStore = useTemplateStore()
|
||||
|
||||
// Initialize templates
|
||||
onMounted(async () => {
|
||||
await templateStore.loadTemplates()
|
||||
})
|
||||
|
||||
const templates = computed(() => templateStore.templates)
|
||||
|
||||
// Template detail view
|
||||
const showTemplateDetail = ref(false)
|
||||
const selectedTemplate = ref(null as any)
|
||||
|
||||
// Execute template
|
||||
const showExecuteDialog = ref(false)
|
||||
const executePath = ref('')
|
||||
const baseDirNames = ref('')
|
||||
const executeFormValid = ref(false)
|
||||
|
||||
const templateTree = computed(() => {
|
||||
if (!selectedTemplate.value) return []
|
||||
|
||||
const items = []
|
||||
|
||||
// Add directories
|
||||
selectedTemplate.value.directories.forEach((dir: any) => {
|
||||
items.push({
|
||||
id: `dir-${dir.path}`,
|
||||
name: dir.path,
|
||||
type: 'directory',
|
||||
children: []
|
||||
})
|
||||
})
|
||||
|
||||
// Add files
|
||||
selectedTemplate.value.files.forEach((file: any) => {
|
||||
items.push({
|
||||
id: `file-${file.path}`,
|
||||
name: file.path,
|
||||
type: 'file'
|
||||
})
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
const navigateToCreateTemplate = () => {
|
||||
router.push('/create-template')
|
||||
}
|
||||
|
||||
const viewTemplate = (template: any) => {
|
||||
selectedTemplate.value = template
|
||||
showTemplateDetail.value = true
|
||||
}
|
||||
|
||||
const executeTemplate = (template: any) => {
|
||||
selectedTemplate.value = template
|
||||
executePath.value = ''
|
||||
baseDirNames.value = ''
|
||||
showExecuteDialog.value = true
|
||||
}
|
||||
|
||||
const selectDirectory = async () => {
|
||||
const result = await window.mainApi.invoke('msgSelectDirectory')
|
||||
if (!result.canceled) {
|
||||
executePath.value = result.filePath
|
||||
}
|
||||
}
|
||||
|
||||
const confirmExecuteTemplate = () => {
|
||||
if (!executePath.value || !baseDirNames.value.trim()) return
|
||||
|
||||
const dirNames = baseDirNames.value.split('\n').filter((name) => name.trim())
|
||||
|
||||
// Call main process to execute template
|
||||
window.mainApi
|
||||
.invoke(
|
||||
'msgExecuteTemplate',
|
||||
selectedTemplate.value.id,
|
||||
executePath.value,
|
||||
dirNames
|
||||
)
|
||||
.then((result: any) => {
|
||||
if (result.success) {
|
||||
alert('模板执行成功!')
|
||||
showExecuteDialog.value = false
|
||||
} else {
|
||||
alert(`模板执行失败: ${result.error}`)
|
||||
}
|
||||
})
|
||||
.catch((error: any) => {
|
||||
alert(`模板执行出错: ${error.message}`)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
Reference in new issue
Block a user