wanshenmean
2026-03-13 58376d519aeb76ef78d38b737c0c57f8d982fb52
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/Pages/Index.cshtml
@@ -1,10 +1,226 @@
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
    ViewData["Title"] = "实例列表";
}
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<div class="d-flex justify-content-between align-items-center mb-4">
    <div>
        <h2 class="mb-0">
            <i class="bi bi-cpu-fill me-2"></i>S7 PLC 仿真器实例
        </h2>
        <p class="text-muted mb-0 mt-1">管理和监控 S7 PLC 仿真器实例</p>
</div>
    <a asp-page="/Create" class="btn btn-primary">
        <i class="bi bi-plus-lg me-1"></i>创建实例
    </a>
</div>
<div id="instancesContainer">
    <!-- Loading state -->
    <div id="loadingState" class="text-center py-5">
        <div class="spinner-border text-primary" role="status">
            <span class="visually-hidden">加载中...</span>
        </div>
        <p class="mt-3 text-muted">正在加载实例列表...</p>
    </div>
    <!-- Empty state -->
    <div id="emptyState" class="empty-state d-none">
        <i class="bi bi-inbox"></i>
        <h3>暂无实例</h3>
        <p>点击上方"创建实例"按钮来创建您的第一个仿真器实例</p>
    </div>
    <!-- Instances grid -->
    <div id="instancesGrid" class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4 d-none"></div>
</div>
@section Scripts {
<script>
    let autoRefreshInterval = null;
    document.addEventListener('DOMContentLoaded', function () {
        loadInstances();
        startAutoRefresh(loadInstances, 5000);
    });
    async function loadInstances() {
        try {
            const instances = await apiCall(`${API_BASE_URL}/SimulatorInstances`);
            renderInstances(instances);
        } catch (error) {
            console.error('Failed to load instances:', error);
            document.getElementById('loadingState').classList.add('d-none');
            document.getElementById('emptyState').classList.remove('d-none');
        }
    }
    function renderInstances(instances) {
        const loadingState = document.getElementById('loadingState');
        const emptyState = document.getElementById('emptyState');
        const grid = document.getElementById('instancesGrid');
        loadingState.classList.add('d-none');
        grid.classList.add('d-none');
        emptyState.classList.add('d-none');
        if (!instances || instances.length === 0) {
            emptyState.classList.remove('d-none');
            return;
        }
        grid.classList.remove('d-none');
        grid.innerHTML = instances.map(instance => createInstanceCard(instance)).join('');
    }
    function createInstanceCard(instance) {
        const statusClass = getStatusClass(instance.status);
        const statusText = getStatusText(instance.status);
        const plcTypeText = getPlcTypeText(instance.plcType);
        const isRunning = instance.status === 'Running';
        const isStopped = instance.status === 'Stopped';
        return `
            <div class="col">
                <div class="card instance-card h-100 ${statusClass}">
                    <div class="card-header d-flex justify-content-between align-items-center">
                        <h5 class="card-title mb-0">${escapeHtml(instance.instanceId)}</h5>
                        <span class="badge ${statusClass}">${statusText}</span>
                    </div>
                    <div class="card-body">
                        <div class="instance-info mb-3">
                            <div class="row mb-2">
                                <div class="col-6">
                                    <small class="instance-info-label">名称</small>
                                    <div class="instance-info-value">${escapeHtml(instance.name || '-')}</div>
                                </div>
                                <div class="col-6">
                                    <small class="instance-info-label">PLC型号</small>
                                    <div class="instance-info-value">${plcTypeText}</div>
                                </div>
                            </div>
                            <div class="row mb-2">
                                <div class="col-6">
                                    <small class="instance-info-label">端口</small>
                                    <div class="instance-info-value">${instance.port || '-'}</div>
                                </div>
                                <div class="col-6">
                                    <small class="instance-info-label">客户端</small>
                                    <div class="instance-info-value">
                                        <i class="bi bi-people-fill me-1"></i>${instance.clientCount || 0}
                                    </div>
                                </div>
                            </div>
                            ${instance.startTime ? `
                            <div class="row">
                                <div class="col-12">
                                    <small class="instance-info-label">启动时间</small>
                                    <div class="instance-info-value small">${formatDate(instance.startTime)}</div>
                                </div>
                            </div>
                            ` : ''}
                            ${instance.errorMessage ? `
                            <div class="alert alert-danger alert-sm mt-2 mb-0 py-2 small">
                                <i class="bi bi-exclamation-triangle-fill me-1"></i>${escapeHtml(instance.errorMessage)}
                            </div>
                            ` : ''}
                        </div>
                    </div>
                    <div class="card-footer bg-white">
                        <div class="action-buttons d-flex gap-2">
                            ${isRunning ? `
                                <button class="btn btn-warning btn-sm flex-fill" onclick="stopInstance('${instance.instanceId}')" ${instance.status === 'Stopping' ? 'disabled' : ''}>
                                    <i class="bi bi-stop-fill me-1"></i>停止
                                </button>
                            ` : ''}
                            ${isStopped ? `
                                <button class="btn btn-success btn-sm flex-fill" onclick="startInstance('${instance.instanceId}')" ${instance.status === 'Starting' ? 'disabled' : ''}>
                                    <i class="bi bi-play-fill me-1"></i>启动
                                </button>
                            ` : ''}
                            <a href="/Details?id=${encodeURIComponent(instance.instanceId)}" class="btn btn-info btn-sm text-white flex-fill">
                                <i class="bi bi-info-circle-fill me-1"></i>详情
                            </a>
                            <a href="/Edit?id=${encodeURIComponent(instance.instanceId)}" class="btn btn-primary btn-sm flex-fill">
                                <i class="bi bi-pencil-fill me-1"></i>编辑
                            </a>
                            <button class="btn btn-danger btn-sm" onclick="deleteInstance('${instance.instanceId}')">
                                <i class="bi bi-trash-fill"></i>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        `;
    }
    function getStatusClass(status) {
        const map = {
            'Stopped': 'status-stopped',
            'Starting': 'status-starting',
            'Running': 'status-running',
            'Stopping': 'status-stopping',
            'Error': 'status-error'
        };
        return map[status] || '';
    }
    async function startInstance(id) {
        confirmAction(`确定要启动实例 "${id}" 吗?`, async () => {
            try {
                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/start`, {
                    method: 'POST'
                });
                showToast(`实例 "${id}" 启动命令已发送`, 'success');
                setTimeout(() => loadInstances(), 1000);
            } catch (error) {
                console.error('Failed to start instance:', error);
            }
        });
    }
    async function stopInstance(id) {
        confirmAction(`确定要停止实例 "${id}" 吗?`, async () => {
            try {
                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}/stop`, {
                    method: 'POST'
                });
                showToast(`实例 "${id}" 停止命令已发送`, 'success');
                setTimeout(() => loadInstances(), 1000);
            } catch (error) {
                console.error('Failed to stop instance:', error);
            }
        });
    }
    async function deleteInstance(id) {
        confirmAction(`确定要删除实例 "${id}" 吗?此操作不可撤销!`, async () => {
            try {
                await apiCall(`${API_BASE_URL}/SimulatorInstances/${encodeURIComponent(id)}?deleteConfig=true`, {
                    method: 'DELETE'
                });
                showToast(`实例 "${id}" 已删除`, 'success');
                loadInstances();
            } catch (error) {
                console.error('Failed to delete instance:', error);
            }
        });
    }
    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
    // Stop auto-refresh when page is hidden
    document.addEventListener('visibilitychange', function () {
        if (document.hidden) {
            stopAutoRefresh();
        } else {
            startAutoRefresh(loadInstances, 5000);
        }
    });
</script>
}