Files
gdt-tool/src/renderer/screens/TemplateListScreen.vue
T
zhanggy 5d7d31c04d feat(模板): 添加模板复制功能并优化文件操作
- 在模板列表和创建模板界面添加复制模板功能
- 将模板卡片样式改为outlined并修正节点统计标签
- 将同步文件操作改为异步操作提升性能
- 在执行模板对话框显示当前模板名称
2025-10-31 18:42:31 +08:00

215 lines
6.5 KiB
Vue

<template>
<v-container>
<v-row>
<v-col cols="12">
<div class="d-flex align-center mb-4 justify-space-between flex-wrap">
<h2 class="mr-2 mb-2"> 文件创建 </h2>
<v-btn @click="navigateToCreateTemplate" color="primary" prepend-icon="mdi-plus" class="mb-2">
创建模板
</v-btn>
</div>
<!-- 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="default" @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" sm="6" md="4" lg="3">
<TemplateCard
:template="template"
@view="viewTemplate"
@execute="executeTemplate"
@edit="editTemplate"
@copy="copyTemplate"
@delete="deleteTemplate"
/>
</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-card-title>
<v-card-text>
<TemplateTree
:items="templateTree"
:height="400"
:read-only="true"
:empty-text="'模板结构为空'"
:expanded="expandedTreeNodes"
@update:expanded="expandedTreeNodes = $event"
/>
</v-card-text>
</v-card>
</v-dialog>
<!-- Execute template dialog -->
<v-dialog v-model="showExecuteDialog" max-width="600">
<v-card>
<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"
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>
</v-container>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useTemplateStore } from '@/renderer/store/template'
import { TreeNode, Template } from '@/types'
import TemplateTree from '@/renderer/components/TemplateTree.vue'
import TemplateCard from '@/renderer/components/TemplateCard.vue'
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
const router = useRouter()
const templateStore = useTemplateStore()
// Initialize templates
onMounted(async () => {
await templateStore.loadTemplates()
})
const { deleteTemplate: storeDeleteTemplate } = templateStore
const templates = computed(() => templateStore.templates)
// Template detail view
const showTemplateDetail = ref(false)
const selectedTemplate = ref<Template | null>(null)
const expandedTreeNodes = ref<string[]>([])
// Execute template
const showExecuteDialog = ref(false)
const executePath = ref('')
const baseDirNames = ref('')
const executeFormValid = ref(false)
const templateTree = computed<TreeNode[]>(() => {
if (!selectedTemplate.value) return []
// 直接返回模板的树状结构
return selectedTemplate.value.treeStructure || []
})
const navigateToCreateTemplate = () => {
router.push('/create-template')
}
const viewTemplate = (template: Template) => {
selectedTemplate.value = template
showTemplateDetail.value = true
}
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: '删除模板',
message: `确定要删除模板 "${template.name}" 吗?此操作不可撤销。`,
confirmText: '删除',
cancelText: '取消',
confirmColor: 'error'
})
if (confirmed) {
storeDeleteTemplate(template.id)
}
}
const executeTemplate = (template: Template) => {
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() || !selectedTemplate.value) 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(async (result: any) => {
if (result.success) {
await ConfirmDialogService.success({
title: '执行成功',
message: '模板执行成功!',
confirmText: '确定'
})
showExecuteDialog.value = false
} else {
await ConfirmDialogService.error({
title: '执行失败',
message: `模板执行失败: ${result.error}`,
confirmText: '确定'
})
}
})
.catch(async (error: any) => {
await ConfirmDialogService.error({
title: '执行出错',
message: `模板执行出错: ${error.message}`,
confirmText: '确定'
})
})
}
</script>