From 80afa37a67b3d7f4c77330a3ca17b616206e4ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9C=B1?= <10714957+xiao-zhu245@user.noreply.gitee.com> Date: Sat, 9 Aug 2025 18:16:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=AF=E5=8A=A8=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ConfirmStartDialog.tsx | 147 +++++++++++++++++++ client/src/pages/InstanceManagerPage.tsx | 54 ++++++- 2 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 client/src/components/ConfirmStartDialog.tsx diff --git a/client/src/components/ConfirmStartDialog.tsx b/client/src/components/ConfirmStartDialog.tsx new file mode 100644 index 0000000..016979b --- /dev/null +++ b/client/src/components/ConfirmStartDialog.tsx @@ -0,0 +1,147 @@ +import React, { useState, useEffect } from 'react' +import { X, AlertTriangle, Terminal, Play } from 'lucide-react' + +interface ConfirmStartDialogProps { + isOpen: boolean + instanceName: string + startCommand: string + onConfirm: () => void + onCancel: () => void +} + +export const ConfirmStartDialog: React.FC = ({ + isOpen, + instanceName, + startCommand, + onConfirm, + onCancel +}) => { + const [isVisible, setIsVisible] = useState(false) + const [isAnimating, setIsAnimating] = useState(false) + const [isClosing, setIsClosing] = useState(false) + + useEffect(() => { + if (isOpen) { + setIsClosing(false) + setIsVisible(true) + setTimeout(() => setIsAnimating(true), 10) + } else { + setIsAnimating(false) + setIsClosing(true) + setTimeout(() => setIsVisible(false), 300) + } + }, [isOpen]) + + const handleCancel = () => { + setIsAnimating(false) + setIsClosing(true) + setTimeout(() => { + onCancel() + }, 300) + } + + const handleConfirm = () => { + setIsAnimating(false) + setIsClosing(true) + setTimeout(() => { + onConfirm() + }, 300) + } + + if (!isVisible) return null + + // 检测启动命令是否为none + const isCommandSuspicious = startCommand === 'none' + + return ( +
+ {/* 背景遮罩 */} +
+ + {/* 对话框内容 */} +
+ {/* 关闭按钮 */} + + + {/* 标题和图标 */} +
+
+ +
+
+

+ 启动命令警告 +

+
+
+ + {/* 实例信息 */} +
+

+ 实例 "{instanceName}" 的启动命令可能存在问题: +

+ + {/* 启动命令信息 */} +
+
+ + 启动命令: +
+

+ {startCommand} +

+
+ + {/* 警告信息 */} +
+
+ +
+

+ 检测到以下问题: +

+
    +
  • • 启动命令为 "none",则代表无启动命令,需要自行查询启动命令否则将无法启动
  • +
+

+ 建议检查启动命令是否正确,或者修改实例配置后再启动。 +

+
+
+
+
+ + {/* 操作按钮 */} +
+ + +
+
+
+ ) +} + +export default ConfirmStartDialog diff --git a/client/src/pages/InstanceManagerPage.tsx b/client/src/pages/InstanceManagerPage.tsx index bad8998..0ede9d0 100644 --- a/client/src/pages/InstanceManagerPage.tsx +++ b/client/src/pages/InstanceManagerPage.tsx @@ -24,6 +24,7 @@ import { Instance, CreateInstanceRequest } from '@/types' import { useNotificationStore } from '@/stores/notificationStore' import apiClient from '@/utils/api' import { ConfirmDeleteDialog } from '@/components/ConfirmDeleteDialog' +import { ConfirmStartDialog } from '@/components/ConfirmStartDialog' import { CreateConfigDialog } from '@/components/CreateConfigDialog' import SearchableSelect from '@/components/SearchableSelect' import RconConsole from '@/components/RconConsole' @@ -77,6 +78,8 @@ const InstanceManagerPage: React.FC = () => { const [editingInstance, setEditingInstance] = useState(null) const [showDeleteDialog, setShowDeleteDialog] = useState(false) const [instanceToDelete, setInstanceToDelete] = useState(null) + const [showStartConfirmDialog, setShowStartConfirmDialog] = useState(false) + const [instanceToStart, setInstanceToStart] = useState(null) const [showCreateConfigDialog, setShowCreateConfigDialog] = useState(false) const [createConfigInfo, setCreateConfigInfo] = useState<{ instanceId: string @@ -607,8 +610,23 @@ const InstanceManagerPage: React.FC = () => { setTimeout(() => setInstallModalAnimating(true), 10) } - // 启动实例 - const handleStartInstance = async (instance: Instance) => { + // 检查启动命令并显示确认对话框 + const handleStartInstance = (instance: Instance) => { + // 检测启动命令是否为none + const isCommandNone = instance.startCommand === 'none' + + if (isCommandNone) { + // 显示确认对话框 + setInstanceToStart(instance) + setShowStartConfirmDialog(true) + } else { + // 直接启动 + performStartInstance(instance) + } + } + + // 实际执行启动实例的函数 + const performStartInstance = async (instance: Instance) => { try { const response = await apiClient.startInstance(instance.id) if (response.success) { @@ -617,7 +635,7 @@ const InstanceManagerPage: React.FC = () => { title: '启动成功', message: `实例 "${instance.name}" 正在启动` }) - + // 如果返回了终端会话ID,使用sessionId参数跳转到终端页面 if (response.data?.terminalSessionId) { navigate(`/terminal?sessionId=${response.data.terminalSessionId}&instance=${instance.id}&cwd=${encodeURIComponent(instance.workingDirectory)}`) @@ -625,12 +643,12 @@ const InstanceManagerPage: React.FC = () => { // 兼容旧版本,使用instance参数 navigate(`/terminal?instance=${instance.id}&cwd=${encodeURIComponent(instance.workingDirectory)}`) } - + fetchInstances() } } catch (error: any) { console.error('启动实例失败:', error) - + // 获取具体的错误消息 let errorMessage = '无法启动实例' if (error.message) { @@ -638,7 +656,7 @@ const InstanceManagerPage: React.FC = () => { } else if (error.error) { errorMessage = error.error } - + addNotification({ type: 'error', title: '启动失败', @@ -647,6 +665,21 @@ const InstanceManagerPage: React.FC = () => { } } + // 确认启动实例 + const handleConfirmStart = () => { + if (instanceToStart) { + performStartInstance(instanceToStart) + setShowStartConfirmDialog(false) + setInstanceToStart(null) + } + } + + // 取消启动实例 + const handleCancelStart = () => { + setShowStartConfirmDialog(false) + setInstanceToStart(null) + } + // 停止实例 const handleStopInstance = async (instance: Instance) => { try { @@ -1935,6 +1968,15 @@ const InstanceManagerPage: React.FC = () => { onCancel={handleCancelDelete} /> + {/* 启动确认对话框 */} + + {/* 启动命令帮助模态框 */} {showStartCommandHelpModal && (