refactor(模板管理): 移除未保存更改检测并提取模板卡片组件
移除CreateTemplateScreen中的未保存更改检测逻辑,简化保存和返回操作 将TemplateListScreen中的模板卡片提取为独立的TemplateCard组件
This commit is contained in:
1 parent
f3e909923a
commit
35a0821153
3 files changed
+109
-103
No files matched your search
@@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<v-card class="d-flex flex-column" height="100%">
|
||||||
|
<v-card-item>
|
||||||
|
<v-card-title>{{ template.name }}</v-card-title>
|
||||||
|
<v-card-subtitle>{{ template.description }}</v-card-subtitle>
|
||||||
|
</v-card-item>
|
||||||
|
|
||||||
|
<v-card-text class="flex-grow-1 py-2">
|
||||||
|
<v-chip-group column>
|
||||||
|
<v-chip size="small" color="primary" variant="tonal">
|
||||||
|
节点: {{ nodeCount.files }}
|
||||||
|
</v-chip>
|
||||||
|
<v-chip size="small" color="success" variant="tonal">
|
||||||
|
目录: {{ nodeCount.directories }}
|
||||||
|
</v-chip>
|
||||||
|
</v-chip-group>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-divider class="mt-auto"></v-divider>
|
||||||
|
|
||||||
|
<v-card-actions class="pa-2 justify-space-between">
|
||||||
|
<div>
|
||||||
|
<v-btn color="primary" variant="text" size="small" @click="$emit('view', template)">
|
||||||
|
查看
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="success" variant="text" size="small" @click="$emit('execute', template)">
|
||||||
|
执行
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-menu location="bottom end">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-btn v-bind="props" icon variant="text" size="small" density="comfortable">
|
||||||
|
<v-icon>mdi-dots-vertical</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item @click="$emit('edit', template)">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon>mdi-pencil</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)">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<v-icon color="error">mdi-delete</v-icon>
|
||||||
|
</template>
|
||||||
|
<v-list-item-title class="text-error">删除</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { Template, TreeNode } from '@/renderer/store/template'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
template: Template
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'view', template: Template): void
|
||||||
|
(e: 'execute', template: Template): void
|
||||||
|
(e: 'edit', template: Template): void
|
||||||
|
(e: 'delete', template: Template): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
// Count files and directories in tree structure
|
||||||
|
const nodeCount = computed(() => {
|
||||||
|
let files = 0
|
||||||
|
let directories = 0
|
||||||
|
|
||||||
|
const count = (nodeList: TreeNode[]) => {
|
||||||
|
nodeList.forEach(node => {
|
||||||
|
if (node.type === 'file') {
|
||||||
|
files++
|
||||||
|
} else if (node.type === 'directory') {
|
||||||
|
directories++
|
||||||
|
if (node.children) {
|
||||||
|
count(node.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
count(props.template.treeStructure || [])
|
||||||
|
|
||||||
|
return { files, directories }
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<v-btn
|
<v-btn
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="saveTemplate"
|
@click="saveTemplate"
|
||||||
:disabled="!formValid || !hasChanges"
|
:disabled="!formValid"
|
||||||
prepend-icon="mdi-content-save"
|
prepend-icon="mdi-content-save"
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
@@ -166,7 +166,6 @@ const { getTemplateById } = templateStore
|
|||||||
const formValid = ref(false)
|
const formValid = ref(false)
|
||||||
const templateName = ref('')
|
const templateName = ref('')
|
||||||
const templateDescription = ref('')
|
const templateDescription = ref('')
|
||||||
const hasChanges = ref(false)
|
|
||||||
|
|
||||||
// Dialogs
|
// Dialogs
|
||||||
const showFileDialog = ref(false)
|
const showFileDialog = ref(false)
|
||||||
@@ -195,14 +194,6 @@ const isEditMode = computed(() => !!route.params.id)
|
|||||||
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
|
const requiredRule = (fieldName: string) => (v: string | null | undefined) =>
|
||||||
!!v || `${fieldName}为必填项`
|
!!v || `${fieldName}为必填项`
|
||||||
|
|
||||||
// Watch for changes to detect unsaved changes
|
|
||||||
watch(
|
|
||||||
[templateName, templateDescription, treeStructure],
|
|
||||||
() => {
|
|
||||||
hasChanges.value = true
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -223,7 +214,6 @@ onMounted(async () => {
|
|||||||
templateName.value = template.name
|
templateName.value = template.name
|
||||||
templateDescription.value = template.description
|
templateDescription.value = template.description
|
||||||
treeStructure.value = [...template.treeStructure]
|
treeStructure.value = [...template.treeStructure]
|
||||||
hasChanges.value = false
|
|
||||||
} else {
|
} else {
|
||||||
router.push('/templates')
|
router.push('/templates')
|
||||||
}
|
}
|
||||||
@@ -431,21 +421,7 @@ const handleFileDialogConfirm = (data: { path: string; sourcePath?: string; sour
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goBack = async () => {
|
const goBack = async () => {
|
||||||
if (hasChanges.value) {
|
|
||||||
const confirmed = await ConfirmDialogService.confirm({
|
|
||||||
title: '确认离开',
|
|
||||||
message: '您有未保存的更改,确定要离开吗?',
|
|
||||||
confirmText: '离开',
|
|
||||||
cancelText: '取消',
|
|
||||||
confirmColor: 'warning'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (confirmed) {
|
|
||||||
router.push('/templates')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
router.push('/templates')
|
router.push('/templates')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -540,7 +516,6 @@ const saveTemplate = () => {
|
|||||||
templateStore.addTemplate(templateData)
|
templateStore.addTemplate(templateData)
|
||||||
}
|
}
|
||||||
|
|
||||||
hasChanges.value = false
|
|
||||||
router.push('/templates')
|
router.push('/templates')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -28,62 +28,13 @@
|
|||||||
<div v-else>
|
<div v-else>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="template in templates" :key="template.id" cols="12" sm="6" md="4" lg="3">
|
<v-col v-for="template in templates" :key="template.id" cols="12" sm="6" md="4" lg="3">
|
||||||
<v-card class="d-flex flex-column" height="100%">
|
<TemplateCard
|
||||||
<v-card-item>
|
:template="template"
|
||||||
<v-card-title>{{ template.name }}</v-card-title>
|
@view="viewTemplate"
|
||||||
<v-card-subtitle>{{ template.description }}</v-card-subtitle>
|
@execute="executeTemplate"
|
||||||
</v-card-item>
|
@edit="editTemplate"
|
||||||
|
@delete="deleteTemplate"
|
||||||
<v-card-text class="flex-grow-1 py-2">
|
/>
|
||||||
<v-chip-group column>
|
|
||||||
<v-chip size="small" color="primary" variant="tonal">
|
|
||||||
节点: {{ countTreeNodes(template.treeStructure).files }}
|
|
||||||
</v-chip>
|
|
||||||
<v-chip size="small" color="success" variant="tonal">
|
|
||||||
目录: {{ countTreeNodes(template.treeStructure).directories }}
|
|
||||||
</v-chip>
|
|
||||||
</v-chip-group>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-divider class="mt-auto"></v-divider>
|
|
||||||
|
|
||||||
<v-card-actions class="pa-2 justify-space-between">
|
|
||||||
<div>
|
|
||||||
<v-btn color="primary" variant="text" size="small" @click="viewTemplate(template)">
|
|
||||||
查看
|
|
||||||
</v-btn>
|
|
||||||
<v-btn color="success" variant="text" size="small" @click="executeTemplate(template)">
|
|
||||||
执行
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-menu location="bottom end">
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-btn v-bind="props" icon variant="text" size="small" density="comfortable">
|
|
||||||
<v-icon>mdi-dots-vertical</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-list density="compact">
|
|
||||||
<v-list-item @click="editTemplate(template)">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-icon>mdi-pencil</v-icon>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title>修改</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-divider></v-divider>
|
|
||||||
|
|
||||||
<v-list-item @click="deleteTemplate(template)">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-icon color="error">mdi-delete</v-icon>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title class="text-error">删除</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-menu>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,6 +92,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { useTemplateStore, TreeNode } from '@/renderer/store/template'
|
import { useTemplateStore, TreeNode } from '@/renderer/store/template'
|
||||||
import { Template } from '@/renderer/store/template'
|
import { Template } from '@/renderer/store/template'
|
||||||
import TemplateTree from '@/renderer/components/TemplateTree.vue'
|
import TemplateTree from '@/renderer/components/TemplateTree.vue'
|
||||||
|
import TemplateCard from '@/renderer/components/TemplateCard.vue'
|
||||||
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
|
import { ConfirmDialogService } from '@/renderer/utils/confirm-dialog-service'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -166,28 +118,7 @@ const executePath = ref('')
|
|||||||
const baseDirNames = ref('')
|
const baseDirNames = ref('')
|
||||||
const executeFormValid = ref(false)
|
const executeFormValid = ref(false)
|
||||||
|
|
||||||
// Count files and directories in tree structure
|
|
||||||
const countTreeNodes = (nodes: TreeNode[]) => {
|
|
||||||
let files = 0;
|
|
||||||
let directories = 0;
|
|
||||||
|
|
||||||
const count = (nodeList: TreeNode[]) => {
|
|
||||||
nodeList.forEach(node => {
|
|
||||||
if (node.type === 'file') {
|
|
||||||
files++;
|
|
||||||
} else if (node.type === 'directory') {
|
|
||||||
directories++;
|
|
||||||
if (node.children) {
|
|
||||||
count(node.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
count(nodes || []);
|
|
||||||
|
|
||||||
return { files, directories };
|
|
||||||
}
|
|
||||||
|
|
||||||
const templateTree = computed<TreeNode[]>(() => {
|
const templateTree = computed<TreeNode[]>(() => {
|
||||||
if (!selectedTemplate.value) return []
|
if (!selectedTemplate.value) return []
|
||||||
|
|||||||
Reference in new issue
Block a user