fix: 修复新建项目无响应问题
- 替换 prompt() 为 InputDialog Vue 模态组件 - 替换 alert()/confirm() 为 Electron showMessageBox IPC - 添加 dialog:showMessage / dialog:showConfirm IPC 通道
This commit is contained in:
100
src/components/InputDialog.vue
Normal file
100
src/components/InputDialog.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="visible" class="dialog-overlay" @click.self="cancel">
|
||||
<div class="dialog-box card">
|
||||
<h3 class="dialog-title">{{ title }}</h3>
|
||||
<p v-if="message" class="dialog-message">{{ message }}</p>
|
||||
<input
|
||||
ref="inputRef"
|
||||
class="dialog-input"
|
||||
v-model="inputValue"
|
||||
:placeholder="placeholder"
|
||||
@keydown.enter="confirm"
|
||||
@keydown.esc="cancel"
|
||||
/>
|
||||
<div class="dialog-actions">
|
||||
<button class="btn btn-secondary" @click="cancel">{{ cancelText }}</button>
|
||||
<button class="btn btn-primary" @click="confirm" :disabled="!inputValue.trim()">{{ okText }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
title: { type: String, default: '输入' },
|
||||
message: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
defaultValue: { type: String, default: '' },
|
||||
cancelText: { type: String, default: '取消' },
|
||||
okText: { type: String, default: '确定' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
const inputValue = ref('')
|
||||
const inputRef = ref(null)
|
||||
|
||||
watch(() => props.visible, (val) => {
|
||||
if (val) {
|
||||
inputValue.value = props.defaultValue
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
inputRef.value?.select()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
function confirm() {
|
||||
if (inputValue.value.trim()) {
|
||||
emit('confirm', inputValue.value.trim())
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emit('cancel')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.dialog-box {
|
||||
width: 400px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.dialog-message {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.dialog-input {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -42,6 +42,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InputDialog
|
||||
:visible="showInput"
|
||||
title="新建项目"
|
||||
message="请输入项目名称"
|
||||
placeholder="例如:MyApp"
|
||||
@confirm="onNameConfirmed"
|
||||
@cancel="onNameCancelled"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -49,25 +58,40 @@
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import InputDialog from '../components/InputDialog.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useProjectStore()
|
||||
const recentProjects = ref(loadRecentProjects())
|
||||
const showInput = ref(false)
|
||||
const pendingDir = ref('')
|
||||
|
||||
async function createNewProject() {
|
||||
const dir = await window.api.dialog.selectDirectory()
|
||||
if (!dir) return
|
||||
const name = prompt('请输入项目名称:')
|
||||
if (!name) return
|
||||
pendingDir.value = dir
|
||||
showInput.value = true
|
||||
}
|
||||
|
||||
async function onNameConfirmed(name) {
|
||||
showInput.value = false
|
||||
try {
|
||||
await store.createProject(name, dir)
|
||||
addRecentProject(dir, name)
|
||||
await store.createProject(name, pendingDir.value)
|
||||
addRecentProject(pendingDir.value, name)
|
||||
router.push('/editor')
|
||||
} catch (err) {
|
||||
alert('创建失败: ' + err.message)
|
||||
await window.api.dialog.showMessage({
|
||||
type: 'error',
|
||||
title: '创建失败',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function onNameCancelled() {
|
||||
showInput.value = false
|
||||
}
|
||||
|
||||
async function openExistingProject() {
|
||||
const dir = await window.api.dialog.selectDirectory()
|
||||
if (!dir) return
|
||||
@@ -80,7 +104,11 @@ async function openProject(dir) {
|
||||
addRecentProject(dir, store.config.name)
|
||||
router.push('/editor')
|
||||
} catch (err) {
|
||||
alert('打开失败: ' + err.message)
|
||||
await window.api.dialog.showMessage({
|
||||
type: 'error',
|
||||
title: '打开失败',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,11 @@ async function importFromFiles() {
|
||||
}
|
||||
|
||||
async function handleDelete(relativePath) {
|
||||
if (confirm(`确定删除 "${relativePath}"?`)) {
|
||||
const ok = await window.api.dialog.showConfirm({
|
||||
title: '删除确认',
|
||||
message: `确定要删除 "${relativePath}" 吗?`
|
||||
})
|
||||
if (ok) {
|
||||
await store.removeFile(relativePath)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user