feat: add @mdi/font and electron-devtools-installer dependencies
- Added @mdi/font for Material Design Icons support. - Included electron-devtools-installer to facilitate Vue DevTools installation in development. refactor: enhance CreateTemplateScreen layout and functionality - Improved layout with a responsive design. - Added functionality for adding directories and files with validation. - Implemented a file content editor dialog for editing file contents. - Updated template save logic to handle changes more effectively. fix: update TemplateListScreen for better UX - Enhanced template list display with improved card layout. - Added action buttons for viewing, executing, editing, and deleting templates. - Improved dialog handling for template details and execution. chore: update vuetify plugin configuration - Removed unused icon set configuration. - Updated locale settings to use simplified Chinese. style: clean up HeaderLayout and vuetify styles - Removed commented-out code and unnecessary styles. - Improved overall styling for better readability and consistency.
This commit is contained in:
1 parent
1aecd5f4b6
commit
4f859a4933
8 files changed
+609
-350
No files matched your search
Vendored
+4
-1
@@ -21,5 +21,8 @@
|
||||
"*.mjs": "javascript",
|
||||
"*.cjs": "javascript",
|
||||
"*.mts": "typescript"
|
||||
}
|
||||
},
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/renderer/locales"
|
||||
]
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.4.47",
|
||||
"electron-log": "^5.4.3",
|
||||
"fs-extra": "^11.3.2",
|
||||
"pinia": "^3.0.3",
|
||||
@@ -61,6 +62,7 @@
|
||||
"dotenv": "^17.2.3",
|
||||
"electron": "^38.3.0",
|
||||
"electron-builder": "^26.0.14",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
"electron-extension-installer": "^2.0.0",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
Generated
+36
-4
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@mdi/font':
|
||||
specifier: ^7.4.47
|
||||
version: 7.4.47
|
||||
electron-log:
|
||||
specifier: ^5.4.3
|
||||
version: 5.4.3
|
||||
@@ -60,6 +63,9 @@ importers:
|
||||
electron-builder:
|
||||
specifier: ^26.0.14
|
||||
version: 26.0.15(electron-builder-squirrel-windows@26.0.15)
|
||||
electron-devtools-installer:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
electron-extension-installer:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(electron@38.3.0)
|
||||
@@ -280,8 +286,8 @@ packages:
|
||||
resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
resolution: {tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2}
|
||||
'@electron/node-gyp@git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
resolution: {commit: 06b29aafb7708acef8b3669835c8a7857ebc92d2, repo: git@github.com:electron/node-gyp.git, type: git}
|
||||
version: 10.2.0-electron.1
|
||||
engines: {node: '>=12.13.0'}
|
||||
hasBin: true
|
||||
@@ -569,6 +575,9 @@ packages:
|
||||
resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@mdi/font@7.4.47':
|
||||
resolution: {integrity: sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==}
|
||||
|
||||
'@mdi/js@7.4.47':
|
||||
resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==}
|
||||
|
||||
@@ -1387,6 +1396,9 @@ packages:
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
electron-devtools-installer@4.0.0:
|
||||
resolution: {integrity: sha512-9Tntu/jtfSn0n6N/ZI6IdvRqXpDyLQiDuuIbsBI+dL+1Ef7C8J2JwByw58P3TJiNeuqyV3ZkphpNWuZK5iSY2w==}
|
||||
|
||||
electron-extension-installer@2.0.0:
|
||||
resolution: {integrity: sha512-dneHKI4zcKRiYJbdO/KP3XxzptlhUqQ/9h4WvIIg4I21rRKr7ZysoRU1vVd7Rgf695Rb5MUJXBwCY2dQm6qHQw==}
|
||||
peerDependencies:
|
||||
@@ -2945,6 +2957,9 @@ packages:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
unzip-crx-3@0.2.0:
|
||||
resolution: {integrity: sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==}
|
||||
|
||||
upath@2.0.1:
|
||||
resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -3133,6 +3148,9 @@ packages:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yaku@0.16.7:
|
||||
resolution: {integrity: sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw==}
|
||||
|
||||
yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
|
||||
@@ -3385,7 +3403,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
'@electron/node-gyp@git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2':
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
exponential-backoff: 3.1.2
|
||||
@@ -3422,7 +3440,7 @@ snapshots:
|
||||
|
||||
'@electron/rebuild@3.7.2':
|
||||
dependencies:
|
||||
'@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2
|
||||
'@electron/node-gyp': git+https://git@github.com:electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2
|
||||
'@malept/cross-spawn-promise': 2.0.0
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.0
|
||||
@@ -3659,6 +3677,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@mdi/font@7.4.47': {}
|
||||
|
||||
'@mdi/js@7.4.47': {}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
@@ -4642,6 +4662,10 @@ snapshots:
|
||||
- electron-builder-squirrel-windows
|
||||
- supports-color
|
||||
|
||||
electron-devtools-installer@4.0.0:
|
||||
dependencies:
|
||||
unzip-crx-3: 0.2.0
|
||||
|
||||
electron-extension-installer@2.0.0(electron@38.3.0):
|
||||
dependencies:
|
||||
electron: 38.3.0
|
||||
@@ -6481,6 +6505,12 @@ snapshots:
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unzip-crx-3@0.2.0:
|
||||
dependencies:
|
||||
jszip: 3.10.1
|
||||
mkdirp: 0.5.6
|
||||
yaku: 0.16.7
|
||||
|
||||
upath@2.0.1: {}
|
||||
|
||||
update-browserslist-db@1.1.3(browserslist@4.24.5):
|
||||
@@ -6662,6 +6692,8 @@ snapshots:
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yaku@0.16.7: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
yallist@4.0.0: {}
|
||||
|
||||
@@ -28,6 +28,18 @@ const initializeMainLogger = () => {
|
||||
app.on('ready', async () => {
|
||||
if (Constants.IS_DEV_ENV) {
|
||||
import('./index.dev')
|
||||
|
||||
// Install Vue DevTools
|
||||
try {
|
||||
const { default: installExtension, VUEJS_DEVTOOLS } = await import('electron-devtools-installer')
|
||||
await installExtension(VUEJS_DEVTOOLS, {
|
||||
loadExtensionOptions: {
|
||||
allowFileAccess: true,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Failed to install Vue DevTools: ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Disable special menus on macOS by uncommenting the following, if necessary
|
||||
|
||||
@@ -32,15 +32,12 @@ const headerMenus: {
|
||||
text: 'title.second',
|
||||
path: '/second'
|
||||
}
|
||||
]
|
||||
]
|
||||
</script>
|
||||
<template>
|
||||
<v-app-bar
|
||||
color="primary"
|
||||
density="compact"
|
||||
>
|
||||
<v-app-bar-title>{{ t(titleKey) }}</v-app-bar-title>
|
||||
<template #append>
|
||||
<v-app-bar color="primary" density="compact">
|
||||
<v-app-bar-title @click="handleRoute('/')">{{ t(titleKey) }}</v-app-bar-title>
|
||||
<!-- <template #append>
|
||||
<v-btn
|
||||
v-for="menu in headerMenus"
|
||||
:key="menu.path"
|
||||
@@ -51,13 +48,14 @@ const headerMenus: {
|
||||
>
|
||||
{{ t(menu.text) }}
|
||||
</v-btn>
|
||||
</template>
|
||||
</template> -->
|
||||
</v-app-bar>
|
||||
</template>
|
||||
<style scoped>
|
||||
.v-btn {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.active {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,17 @@ import {
|
||||
pt,
|
||||
nl
|
||||
} from 'vuetify/locale'
|
||||
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
|
||||
// import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
|
||||
import 'vuetify/styles'
|
||||
import '@mdi/font/css/materialdesignicons.css'
|
||||
|
||||
import colors from 'vuetify/util/colors'
|
||||
|
||||
export default createVuetify({
|
||||
locale: {
|
||||
messages: { ko, en, zhHans, zhHant, de, es, ja, fr, ru, pt, nl },
|
||||
locale: 'en',
|
||||
fallback: 'en'
|
||||
locale: 'zhHans',
|
||||
fallback: 'zhHans'
|
||||
},
|
||||
defaults: {
|
||||
VBtn: {
|
||||
@@ -33,13 +34,13 @@ export default createVuetify({
|
||||
]
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
defaultSet: 'mdi',
|
||||
aliases,
|
||||
sets: {
|
||||
mdi
|
||||
}
|
||||
},
|
||||
// icons: {
|
||||
// defaultSet: 'mdi',
|
||||
// aliases,
|
||||
// sets: {
|
||||
// mdi
|
||||
// }
|
||||
// },
|
||||
theme: {
|
||||
themes: {
|
||||
light: {
|
||||
|
||||
@@ -1,155 +1,233 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<h2 class="mb-4">
|
||||
{{ templateId ? '编辑模板' : '创建模板' }}
|
||||
<v-container fluid class="pa-0 fill-height">
|
||||
<v-row class="fill-height">
|
||||
<!-- Left Panel - Template Info -->
|
||||
<v-col cols="12" md="4" class="pa-4 border-e">
|
||||
<div class="d-flex align-center mb-4">
|
||||
<h2 class="text-h5 mr-2">
|
||||
{{ isEditMode ? '编辑模板' : '创建模板' }}
|
||||
</h2>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
@click="goBack"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="bottom"
|
||||
>
|
||||
关闭
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-form v-model="formValid">
|
||||
<v-form v-model="formValid" class="mb-4">
|
||||
<v-text-field
|
||||
v-model="template.name"
|
||||
v-model="templateName"
|
||||
label="模板名称 *"
|
||||
:rules="[(v) => !!v || '模板名称不能为空']"
|
||||
placeholder="输入模板名称"
|
||||
:rules="[requiredRule]"
|
||||
required
|
||||
data-testid="template-name-input"
|
||||
clearable
|
||||
density="comfortable"
|
||||
/>
|
||||
|
||||
<v-textarea
|
||||
v-model="template.description"
|
||||
v-model="templateDescription"
|
||||
label="模板描述"
|
||||
placeholder="描述这个模板的用途..."
|
||||
rows="2"
|
||||
data-testid="template-description-input"
|
||||
auto-grow
|
||||
clearable
|
||||
density="comfortable"
|
||||
/>
|
||||
</v-form>
|
||||
|
||||
<!-- Template content -->
|
||||
<v-card class="mb-4">
|
||||
<v-card-title>模板内容</v-card-title>
|
||||
<v-card-text>
|
||||
<!-- Files section -->
|
||||
<v-expansion-panels class="mb-4">
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>文件</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-data-table
|
||||
:headers="fileHeaders"
|
||||
:items="template.files"
|
||||
class="elevation-1"
|
||||
>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon
|
||||
color="error"
|
||||
@click="removeFile(item)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-divider class="my-4" />
|
||||
|
||||
<v-row class="mt-4">
|
||||
<v-col cols="12">
|
||||
<!-- File Operations -->
|
||||
<div class="mb-4">
|
||||
<h3 class="text-subtitle-1 mb-2">添加项</h3>
|
||||
<div class="d-flex flex-column ga-2">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="showAddFileDialog = true"
|
||||
prepend-icon="mdi-file-plus"
|
||||
>
|
||||
添加文件
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
|
||||
<!-- Directories section -->
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>目录</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-data-table
|
||||
:headers="dirHeaders"
|
||||
:items="template.directories"
|
||||
class="elevation-1"
|
||||
>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
icon
|
||||
color="error"
|
||||
@click="removeDirectory(item)"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<v-row class="mt-4">
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="showAddDirDialog = true"
|
||||
variant="outlined"
|
||||
prepend-icon="mdi-folder-plus"
|
||||
@click="showAddDirectoryDialog"
|
||||
block
|
||||
>
|
||||
添加目录
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-btn
|
||||
:color="templateId ? 'warning' : 'success'"
|
||||
color="success"
|
||||
variant="outlined"
|
||||
prepend-icon="mdi-file-plus"
|
||||
@click="showAddFileDialog"
|
||||
block
|
||||
>
|
||||
添加文件
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-divider class="my-4" />
|
||||
|
||||
<!-- Template Actions -->
|
||||
<div class="d-flex ga-2">
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="saveTemplate"
|
||||
:disabled="!formValid"
|
||||
:disabled="!formValid || !hasChanges"
|
||||
prepend-icon="mdi-content-save"
|
||||
block
|
||||
>
|
||||
{{ templateId ? '更新模板' : '保存模板' }}
|
||||
保存模板
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<!-- Right Panel - Tree Structure -->
|
||||
<v-col cols="12" md="8" class="pa-4">
|
||||
<div class="d-flex align-center mb-4">
|
||||
<h3 class="text-h6 mr-2">模板结构</h3>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
@click="expandAll"
|
||||
size="small"
|
||||
>
|
||||
<v-icon>mdi-arrow-expand-down</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="bottom"
|
||||
>
|
||||
展开全部
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
@click="cancel"
|
||||
prepend-icon="mdi-arrow-left"
|
||||
icon
|
||||
variant="text"
|
||||
@click="collapseAll"
|
||||
size="small"
|
||||
>
|
||||
返回
|
||||
<v-icon>mdi-arrow-collapse-up</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="bottom"
|
||||
>
|
||||
收起全部
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</div>
|
||||
|
||||
<v-card
|
||||
variant="outlined"
|
||||
class="pa-2"
|
||||
:height="treeHeight"
|
||||
>
|
||||
<v-treeview
|
||||
v-model:expanded="expandedNodes"
|
||||
:items="treeItems"
|
||||
item-title="name"
|
||||
item-key="id"
|
||||
item-children="children"
|
||||
activatable
|
||||
open-on-click
|
||||
:return-object="false"
|
||||
>
|
||||
<template v-slot:prepend="{ item }">
|
||||
<v-icon
|
||||
:color="item.type === 'directory' ? 'warning' : 'info'"
|
||||
size="large"
|
||||
>
|
||||
{{
|
||||
item.type === 'directory'
|
||||
? 'mdi-folder-outline'
|
||||
: 'mdi-file-document-outline'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
|
||||
<template v-slot:append="{ item }">
|
||||
<div class="d-flex align-center ga-1">
|
||||
<v-btn
|
||||
v-if="item.type === 'file'"
|
||||
icon
|
||||
variant="text"
|
||||
size="small"
|
||||
@click.stop="editFile(item)"
|
||||
>
|
||||
<v-icon size="small">mdi-pencil</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
>
|
||||
编辑内容
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
|
||||
<v-btn
|
||||
icon
|
||||
variant="text"
|
||||
size="small"
|
||||
@click.stop="deleteItem(item)"
|
||||
>
|
||||
<v-icon
|
||||
size="small"
|
||||
color="error"
|
||||
>
|
||||
mdi-delete
|
||||
</v-icon>
|
||||
<v-tooltip
|
||||
activator="parent"
|
||||
location="top"
|
||||
>
|
||||
删除
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-slot:no-data>
|
||||
<div class="text-center pa-4">
|
||||
<v-icon size="48" color="grey-lighten-1">mdi-folder-open-outline</v-icon>
|
||||
<p class="text-body-2 mt-2">暂无文件或目录</p>
|
||||
<p class="text-caption">点击左侧"添加目录"或"添加文件"开始创建</p>
|
||||
</div>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- Add file dialog -->
|
||||
<v-dialog
|
||||
v-model="showAddFileDialog"
|
||||
max-width="600"
|
||||
>
|
||||
<!-- Add Directory Dialog -->
|
||||
<v-dialog v-model="showDirectoryDialog" max-width="500">
|
||||
<v-card>
|
||||
<v-card-title>添加文件</v-card-title>
|
||||
<v-card-title>添加目录</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="fileFormValid">
|
||||
<v-form v-model="directoryFormValid">
|
||||
<v-text-field
|
||||
v-model="newFile.path"
|
||||
label="文件路径 *"
|
||||
placeholder="例如: src/index.js"
|
||||
:rules="[(v) => !!v || '文件路径不能为空']"
|
||||
data-testid="add-file-path-input"
|
||||
/>
|
||||
|
||||
<v-textarea
|
||||
v-model="newFile.content"
|
||||
label="文件内容"
|
||||
placeholder="输入文件内容..."
|
||||
rows="6"
|
||||
data-testid="add-file-content-input"
|
||||
v-model="newDirectoryPath"
|
||||
label="目录路径 *"
|
||||
placeholder="例如: src/components"
|
||||
:rules="[requiredRule]"
|
||||
required
|
||||
density="comfortable"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showAddFileDialog = false"> 取消 </v-btn>
|
||||
<v-btn @click="showDirectoryDialog = false">
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="addFile"
|
||||
:disabled="!fileFormValid"
|
||||
@click="addDirectory"
|
||||
:disabled="!directoryFormValid"
|
||||
>
|
||||
添加
|
||||
</v-btn>
|
||||
@@ -157,178 +235,323 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Add directory dialog -->
|
||||
<v-dialog
|
||||
v-model="showAddDirDialog"
|
||||
max-width="600"
|
||||
>
|
||||
<!-- Add File Dialog -->
|
||||
<v-dialog v-model="showFileDialog" max-width="500">
|
||||
<v-card>
|
||||
<v-card-title>添加目录</v-card-title>
|
||||
<v-card-title>添加文件</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="dirFormValid">
|
||||
<v-form v-model="fileFormValid">
|
||||
<v-text-field
|
||||
v-model="newDir.path"
|
||||
label="目录路径 *"
|
||||
placeholder="例如: src/components"
|
||||
:rules="[(v) => !!v || '目录路径不能为空']"
|
||||
data-testid="add-dir-path-input"
|
||||
v-model="newFilePath"
|
||||
label="文件路径 *"
|
||||
placeholder="例如: src/index.js"
|
||||
:rules="[requiredRule]"
|
||||
required
|
||||
density="comfortable"
|
||||
/>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="showAddDirDialog = false"> 取消 </v-btn>
|
||||
<v-btn @click="showFileDialog = false">
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="addDirectory"
|
||||
:disabled="!dirFormValid"
|
||||
@click="addFile"
|
||||
:disabled="!fileFormValid"
|
||||
>
|
||||
添加
|
||||
添加并编辑
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- File Content Editor Dialog -->
|
||||
<v-dialog v-model="showEditorDialog" fullscreen>
|
||||
<v-card>
|
||||
<v-toolbar dark color="primary">
|
||||
<v-btn icon @click="showEditorDialog = false">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title>
|
||||
编辑文件: {{ editingFile?.path }}
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="text"
|
||||
@click="saveFileContent"
|
||||
>
|
||||
保存
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card-text class="pa-0 fill-height">
|
||||
<v-textarea
|
||||
v-model="editingFileContent"
|
||||
class="fill-height"
|
||||
style="height: calc(100vh - 120px);"
|
||||
no-resize
|
||||
spellcheck="false"
|
||||
placeholder="在此输入文件内容..."
|
||||
variant="solo-filled"
|
||||
density="comfortable"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useTemplateStore } from '@/renderer/store/template'
|
||||
import { Template } from '@/renderer/store/template'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const templateStore = useTemplateStore()
|
||||
|
||||
// Form validation
|
||||
const formValid = ref(false)
|
||||
const fileFormValid = ref(false)
|
||||
const dirFormValid = ref(false)
|
||||
// Extract methods from store
|
||||
const { getTemplateById } = templateStore
|
||||
|
||||
// Template data
|
||||
const templateId = ref(route.params.id as string)
|
||||
const template = ref({
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
files: [] as any[],
|
||||
directories: [] as any[]
|
||||
})
|
||||
// Form state
|
||||
const formValid = ref(false)
|
||||
const templateName = ref('')
|
||||
const templateDescription = ref('')
|
||||
const hasChanges = ref(false)
|
||||
|
||||
// Dialogs
|
||||
const showAddFileDialog = ref(false)
|
||||
const showAddDirDialog = ref(false)
|
||||
const showDirectoryDialog = ref(false)
|
||||
const showFileDialog = ref(false)
|
||||
const showEditorDialog = ref(false)
|
||||
const directoryFormValid = ref(false)
|
||||
const fileFormValid = ref(false)
|
||||
|
||||
// New file data
|
||||
const newFile = ref({
|
||||
path: '',
|
||||
content: ''
|
||||
// New item state
|
||||
const newDirectoryPath = ref('')
|
||||
const newFilePath = ref('')
|
||||
const editingFile = ref<{ path: string; content: string } | null>(null)
|
||||
const editingFileContent = ref('')
|
||||
|
||||
// Tree state
|
||||
const expandedNodes = ref<string[]>([])
|
||||
const treeHeight = ref(600)
|
||||
|
||||
// Item state
|
||||
const directories = ref<{ path: string }[]>([])
|
||||
const files = ref<{ path: string; content: string }[]>([])
|
||||
|
||||
// Computed
|
||||
const isEditMode = computed(() => !!route.params.id)
|
||||
const treeItems = computed(() => {
|
||||
const items = []
|
||||
|
||||
// Add directories
|
||||
directories.value.forEach(dir => {
|
||||
items.push({
|
||||
id: `dir-${dir.path}`,
|
||||
name: dir.path,
|
||||
type: 'directory',
|
||||
children: []
|
||||
})
|
||||
})
|
||||
|
||||
// Add files
|
||||
files.value.forEach(file => {
|
||||
items.push({
|
||||
id: `file-${file.path}`,
|
||||
name: file.path,
|
||||
type: 'file'
|
||||
})
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
// New directory data
|
||||
const newDir = ref({
|
||||
path: ''
|
||||
})
|
||||
// Validation rules
|
||||
const requiredRule = (v: any) => !!v || '此字段为必填项'
|
||||
|
||||
// Table headers
|
||||
const fileHeaders = ref([
|
||||
{ title: '文件路径', key: 'path' },
|
||||
{ title: '操作', key: 'actions', sortable: false }
|
||||
])
|
||||
|
||||
const dirHeaders = ref([
|
||||
{ title: '目录路径', key: 'path' },
|
||||
{ title: '操作', key: 'actions', sortable: false }
|
||||
])
|
||||
// Watch for changes to detect unsaved changes
|
||||
watch([templateName, templateDescription, directories, files], () => {
|
||||
hasChanges.value = true
|
||||
}, { deep: true })
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
await templateStore.loadTemplates()
|
||||
|
||||
if (templateId.value) {
|
||||
// Edit existing template
|
||||
const existingTemplate = templateStore.getTemplateById(templateId.value)
|
||||
if (existingTemplate) {
|
||||
template.value = { ...existingTemplate }
|
||||
// Set tree height based on window height
|
||||
updateTreeHeight()
|
||||
|
||||
// Add resize listener
|
||||
window.addEventListener('resize', updateTreeHeight)
|
||||
|
||||
// Handle edit mode
|
||||
if (isEditMode.value) {
|
||||
const templateId = route.params.id as string
|
||||
const template = getTemplateById(templateId)
|
||||
|
||||
if (template) {
|
||||
templateName.value = template.name
|
||||
templateDescription.value = template.description
|
||||
directories.value = [...template.directories]
|
||||
files.value = [...template.files]
|
||||
hasChanges.value = false
|
||||
} else {
|
||||
// If template doesn't exist, redirect to template list
|
||||
router.push('/templates')
|
||||
}
|
||||
}
|
||||
|
||||
// Expand all nodes by default
|
||||
expandAll()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateTreeHeight)
|
||||
})
|
||||
|
||||
// Methods
|
||||
const updateTreeHeight = () => {
|
||||
treeHeight.value = window.innerHeight - 200
|
||||
}
|
||||
|
||||
// Methods
|
||||
const goBack = () => {
|
||||
if (hasChanges.value) {
|
||||
if (confirm('您有未保存的更改,确定要离开吗?')) {
|
||||
router.push('/templates')
|
||||
}
|
||||
} else {
|
||||
// New template
|
||||
template.value = {
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
files: [],
|
||||
directories: []
|
||||
router.push('/templates')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const addFile = () => {
|
||||
if (!newFile.value.path.trim()) return
|
||||
|
||||
// Check for duplicate file paths
|
||||
const exists = template.value.files.some((f) => f.path === newFile.value.path)
|
||||
if (exists) {
|
||||
alert('该文件路径已存在!')
|
||||
return
|
||||
}
|
||||
|
||||
template.value.files.push({
|
||||
path: newFile.value.path,
|
||||
content: newFile.value.content
|
||||
})
|
||||
|
||||
newFile.value = { path: '', content: '' }
|
||||
showAddFileDialog.value = false
|
||||
}
|
||||
|
||||
const removeFile = (file: any) => {
|
||||
template.value.files = template.value.files.filter((f) => f !== file)
|
||||
const expandAll = () => {
|
||||
expandedNodes.value = treeItems.value.map(item => item.id)
|
||||
}
|
||||
|
||||
const collapseAll = () => {
|
||||
expandedNodes.value = []
|
||||
}
|
||||
|
||||
const showAddDirectoryDialog = () => {
|
||||
newDirectoryPath.value = ''
|
||||
showDirectoryDialog.value = true
|
||||
}
|
||||
|
||||
const showAddFileDialog = () => {
|
||||
newFilePath.value = ''
|
||||
showFileDialog.value = true
|
||||
}
|
||||
|
||||
const addDirectory = () => {
|
||||
if (!newDir.value.path.trim()) return
|
||||
if (!newDirectoryPath.value.trim()) return
|
||||
|
||||
// Check for duplicate directory paths
|
||||
const exists = template.value.directories.some(
|
||||
(d) => d.path === newDir.value.path
|
||||
)
|
||||
// Check for duplicates
|
||||
const exists = directories.value.some(d => d.path === newDirectoryPath.value)
|
||||
if (exists) {
|
||||
alert('该目录路径已存在!')
|
||||
return
|
||||
}
|
||||
|
||||
template.value.directories.push({
|
||||
path: newDir.value.path
|
||||
directories.value.push({
|
||||
path: newDirectoryPath.value
|
||||
})
|
||||
|
||||
newDir.value = { path: '' }
|
||||
showAddDirDialog.value = false
|
||||
showDirectoryDialog.value = false
|
||||
newDirectoryPath.value = ''
|
||||
}
|
||||
|
||||
const removeDirectory = (dir: any) => {
|
||||
template.value.directories = template.value.directories.filter(
|
||||
(d) => d !== dir
|
||||
)
|
||||
const addFile = () => {
|
||||
if (!newFilePath.value.trim()) return
|
||||
|
||||
// Check for duplicates
|
||||
const exists = files.value.some(f => f.path === newFilePath.value)
|
||||
if (exists) {
|
||||
alert('该文件路径已存在!')
|
||||
return
|
||||
}
|
||||
|
||||
const newFile = {
|
||||
path: newFilePath.value,
|
||||
content: ''
|
||||
}
|
||||
|
||||
files.value.push(newFile)
|
||||
|
||||
// Open editor for the new file
|
||||
editFileContent(newFile)
|
||||
|
||||
showFileDialog.value = false
|
||||
newFilePath.value = ''
|
||||
}
|
||||
|
||||
const editFile = (item: any) => {
|
||||
const file = files.value.find(f => `file-${f.path}` === item.id)
|
||||
if (file) {
|
||||
editFileContent(file)
|
||||
}
|
||||
}
|
||||
|
||||
const editFileContent = (file: { path: string; content: string }) => {
|
||||
editingFile.value = file
|
||||
editingFileContent.value = file.content
|
||||
showEditorDialog.value = true
|
||||
}
|
||||
|
||||
const saveFileContent = () => {
|
||||
if (editingFile.value) {
|
||||
const index = files.value.findIndex(f => f.path === editingFile.value?.path)
|
||||
if (index !== -1) {
|
||||
files.value[index].content = editingFileContent.value
|
||||
}
|
||||
}
|
||||
showEditorDialog.value = false
|
||||
}
|
||||
|
||||
const deleteItem = (item: any) => {
|
||||
if (!confirm('确定要删除此项吗?')) return
|
||||
|
||||
if (item.type === 'directory') {
|
||||
directories.value = directories.value.filter(d => `dir-${d.path}` !== item.id)
|
||||
} else {
|
||||
files.value = files.value.filter(f => `file-${f.path}` !== item.id)
|
||||
}
|
||||
}
|
||||
|
||||
const saveTemplate = () => {
|
||||
if (!formValid.value) return
|
||||
|
||||
if (templateId.value) {
|
||||
// Update existing template
|
||||
templateStore.updateTemplate(template.value)
|
||||
} else {
|
||||
// Create new template
|
||||
templateStore.addTemplate(template.value)
|
||||
const templateData = {
|
||||
name: templateName.value,
|
||||
description: templateDescription.value,
|
||||
files: files.value,
|
||||
directories: directories.value
|
||||
}
|
||||
|
||||
router.push('/templates')
|
||||
}
|
||||
if (isEditMode.value) {
|
||||
const templateId = route.params.id as string
|
||||
templateStore.updateTemplate({
|
||||
id: templateId,
|
||||
...templateData
|
||||
})
|
||||
} else {
|
||||
templateStore.addTemplate(templateData)
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
hasChanges.value = false
|
||||
router.push('/templates')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.border-e {
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.fill-height {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -2,13 +2,15 @@
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<h2 class="mb-4"> 模板列表 </h2>
|
||||
<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 v-if="templates.length === 0" class="pa-6 mb-4 text-center">
|
||||
<v-card-title class="text-h5 mb-4">
|
||||
暂无模板
|
||||
</v-card-title>
|
||||
@@ -16,12 +18,7 @@
|
||||
<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 color="primary" size="default" @click="navigateToCreateTemplate" prepend-icon="mdi-plus">
|
||||
创建模板
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -30,38 +27,61 @@
|
||||
<!-- 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-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%">
|
||||
<v-card-item>
|
||||
<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-item>
|
||||
|
||||
<v-card-text class="flex-grow-1 py-2">
|
||||
<v-chip-group column>
|
||||
<v-chip size="small" color="primary" variant="tonal">
|
||||
文件: {{ template.files.length }}
|
||||
</v-chip>
|
||||
<v-chip size="small" color="success" variant="tonal">
|
||||
目录: {{ template.directories.length }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
@click="viewTemplate(template)"
|
||||
>
|
||||
|
||||
<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="outlined"
|
||||
@click="executeTemplate(template)"
|
||||
>
|
||||
<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>
|
||||
@@ -71,37 +91,20 @@
|
||||
</v-row>
|
||||
|
||||
<!-- Template detail dialog -->
|
||||
<v-dialog
|
||||
v-model="showTemplateDetail"
|
||||
max-width="800"
|
||||
>
|
||||
<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-treeview :items="templateTree" item-title="name" item-key="id" item-children="children" activatable
|
||||
open-all>
|
||||
<template v-slot:prepend="{ item }">
|
||||
<v-icon>
|
||||
{{
|
||||
item.type === 'directory'
|
||||
? open
|
||||
? 'mdi-folder-open'
|
||||
: 'mdi-folder'
|
||||
? 'mdi-folder'
|
||||
: 'mdi-file'
|
||||
}}
|
||||
</v-icon>
|
||||
@@ -112,63 +115,27 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- Execute template dialog -->
|
||||
<v-dialog
|
||||
v-model="showExecuteDialog"
|
||||
max-width="600"
|
||||
>
|
||||
<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-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 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>
|
||||
|
||||
@@ -176,6 +143,7 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useTemplateStore } from '@/renderer/store/template'
|
||||
import { Template } from '@/renderer/store/template'
|
||||
|
||||
const router = useRouter()
|
||||
const templateStore = useTemplateStore()
|
||||
@@ -185,11 +153,13 @@ onMounted(async () => {
|
||||
await templateStore.loadTemplates()
|
||||
})
|
||||
|
||||
const { deleteTemplate: storeDeleteTemplate } = templateStore
|
||||
|
||||
const templates = computed(() => templateStore.templates)
|
||||
|
||||
// Template detail view
|
||||
const showTemplateDetail = ref(false)
|
||||
const selectedTemplate = ref(null as any)
|
||||
const selectedTemplate = ref<Template | null>(null)
|
||||
|
||||
// Execute template
|
||||
const showExecuteDialog = ref(false)
|
||||
@@ -197,13 +167,21 @@ const executePath = ref('')
|
||||
const baseDirNames = ref('')
|
||||
const executeFormValid = ref(false)
|
||||
|
||||
const templateTree = computed(() => {
|
||||
// Define item type for the tree view
|
||||
interface TreeNode {
|
||||
id: string
|
||||
name: string
|
||||
type: 'directory' | 'file'
|
||||
children?: TreeNode[]
|
||||
}
|
||||
|
||||
const templateTree = computed<TreeNode[]>(() => {
|
||||
if (!selectedTemplate.value) return []
|
||||
|
||||
const items = []
|
||||
const items: TreeNode[] = []
|
||||
|
||||
// Add directories
|
||||
selectedTemplate.value.directories.forEach((dir: any) => {
|
||||
selectedTemplate.value.directories.forEach((dir) => {
|
||||
items.push({
|
||||
id: `dir-${dir.path}`,
|
||||
name: dir.path,
|
||||
@@ -213,7 +191,7 @@ const templateTree = computed(() => {
|
||||
})
|
||||
|
||||
// Add files
|
||||
selectedTemplate.value.files.forEach((file: any) => {
|
||||
selectedTemplate.value.files.forEach((file) => {
|
||||
items.push({
|
||||
id: `file-${file.path}`,
|
||||
name: file.path,
|
||||
@@ -228,12 +206,22 @@ const navigateToCreateTemplate = () => {
|
||||
router.push('/create-template')
|
||||
}
|
||||
|
||||
const viewTemplate = (template: any) => {
|
||||
const viewTemplate = (template: Template) => {
|
||||
selectedTemplate.value = template
|
||||
showTemplateDetail.value = true
|
||||
}
|
||||
|
||||
const executeTemplate = (template: any) => {
|
||||
const editTemplate = (template: Template) => {
|
||||
router.push(`/create-template/${template.id}`)
|
||||
}
|
||||
|
||||
const deleteTemplate = (template: Template) => {
|
||||
if (confirm(`确定要删除模板 "${template.name}" 吗?此操作不可撤销。`)) {
|
||||
storeDeleteTemplate(template.id)
|
||||
}
|
||||
}
|
||||
|
||||
const executeTemplate = (template: Template) => {
|
||||
selectedTemplate.value = template
|
||||
executePath.value = ''
|
||||
baseDirNames.value = ''
|
||||
@@ -248,7 +236,7 @@ const selectDirectory = async () => {
|
||||
}
|
||||
|
||||
const confirmExecuteTemplate = () => {
|
||||
if (!executePath.value || !baseDirNames.value.trim()) return
|
||||
if (!executePath.value || !baseDirNames.value.trim() || !selectedTemplate.value) return
|
||||
|
||||
const dirNames = baseDirNames.value.split('\n').filter((name) => name.trim())
|
||||
|
||||
|
||||
Reference in new issue
Block a user