- 在模板列表和创建模板界面添加复制模板功能 - 将模板卡片样式改为outlined并修正节点统计标签 - 将同步文件操作改为异步操作提升性能 - 在执行模板对话框显示当前模板名称
215 lines
6.5 KiB
Vue
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>
|