380 lines
14 KiB
PHP
380 lines
14 KiB
PHP
@extends('admin.main')
|
|
|
|
@push('style')
|
|
<link rel="stylesheet" href="{{ asset('lib/wmks/css/wmks-all.css') }}" />
|
|
<link rel="stylesheet" href="{{ asset('lib/adminlte/adminlte.css') }}">
|
|
<style>
|
|
/* Loading Animation Styles */
|
|
.loading-container {
|
|
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
|
min-height: 500px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 60px;
|
|
height: 60px;
|
|
border: 4px solid rgba(255, 255, 255, 0.2);
|
|
border-left: 4px solid #007bff;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
.loading-dots {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.loading-dots span {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: #007bff;
|
|
animation: pulse 1.4s ease-in-out infinite both;
|
|
}
|
|
|
|
.loading-dots span:nth-child(1) {
|
|
animation-delay: -0.32s;
|
|
}
|
|
|
|
.loading-dots span:nth-child(2) {
|
|
animation-delay: -0.16s;
|
|
}
|
|
|
|
.loading-dots span:nth-child(3) {
|
|
animation-delay: 0s;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
|
|
0%,
|
|
80%,
|
|
100% {
|
|
transform: scale(0.7);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
40% {
|
|
transform: scale(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.console-container {
|
|
transition: all 0.5s ease-in-out;
|
|
min-height: 500px;
|
|
}
|
|
|
|
.fade-transition {
|
|
transition: opacity 0.4s ease-in-out;
|
|
}
|
|
|
|
|
|
.spinner-box {
|
|
width: 300px;
|
|
height: 300px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background-color: transparent;
|
|
}
|
|
|
|
.three-quarter-spinner {
|
|
width: 50px;
|
|
height: 50px;
|
|
border: 3px solid #fb5b53;
|
|
border-top: 3px solid transparent;
|
|
border-radius: 50%;
|
|
animation: spin .5s linear 0s infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
from {
|
|
transform: rotate(0);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(359deg);
|
|
}
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<!-- Header -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">VMware Console</h3>
|
|
<div class="card-tools">
|
|
<a href="{{ route('customer.product', ['userid' => $userid ?? 0, 'product' => $product->id ?? 0, 'type' => $type ?? 4]) }}"
|
|
class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left"></i> Quay lại
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="mb-0"><strong>VM:</strong> {{ $product->name ?? 'VM Console' }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Control Panel -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Điều khiển Console</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="sendStringText">Nhập văn bản:</label>
|
|
<input id="sendStringText" class="form-control" type="text"
|
|
placeholder="Nhập văn bản...">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label> </label>
|
|
<div class="btn-group d-flex" role="group">
|
|
<button onclick="enterFullScreen()" class="btn btn-primary flex-fill">
|
|
<i class="fas fa-expand"></i> Full Screen
|
|
</button>
|
|
<button onclick="sendCAD()" class="btn btn-success flex-fill">
|
|
<i class="fas fa-keyboard"></i> Ctrl+Alt+Del
|
|
</button>
|
|
<button onclick="sendInputString()" class="btn btn-info flex-fill">
|
|
<i class="fas fa-paper-plane"></i> Gửi văn bản
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Console Container -->
|
|
<div class="card">
|
|
<div class="card-body p-0 position-relative">
|
|
<!-- Loading State -->
|
|
<div id="loadingState"
|
|
class="loading-container d-flex align-items-center justify-content-center text-black fade-transition"
|
|
style="height: 70vh;">
|
|
<div class="text-center">
|
|
<!-- Modern Spinner -->
|
|
<div class="spinner-box">
|
|
<div class="three-quarter-spinner"></div>
|
|
</div>
|
|
|
|
<!-- Loading Text -->
|
|
<h4 class="mb-3 font-weight-bold">{{ __('Đang kết nối đến vCenter') }}</h4>
|
|
<p class="mb-4 text-light" id="loadingText">{{ __('Khởi tạo kết nối Console') }}
|
|
</p>
|
|
|
|
<!-- Loading Dots Animation -->
|
|
<div class="loading-dots justify-content-center">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
|
|
<!-- Connection Status -->
|
|
<div class="mt-4">
|
|
<small class="text-dark opacity-75"
|
|
id="connectionStatus">{{ __('Đang thiết lập phiên làm việc...') }}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Console Display -->
|
|
<div id="wmksContainer"
|
|
class="console-container d-flex justify-content-center align-items-center bg-dark fade-transition"
|
|
style="height: 70vh; opacity: 0; position: absolute; top: 0; left: 0; right: 0;">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
|
|
<script src="{{ asset('lib/jquery-ui/jquery-ui.min.js') }}"></script>
|
|
<script src="{{ asset('lib/wmks/wmks.min.js') }}"></script>
|
|
<script src="{{ asset('lib/wmks/wmks-sdk-example.js') }}"></script>
|
|
|
|
<script type="text/javascript">
|
|
// Loading management variables
|
|
let loadingSteps = [
|
|
'{{ __('Khởi tạo kết nối...') }}',
|
|
'{{ __('Đang xác thực...') }}',
|
|
'{{ __('Thiết lập phiên làm việc...') }}',
|
|
'{{ __('Đang kết nối Console...') }}',
|
|
'{{ __('Sẵn sàng!') }}'
|
|
];
|
|
let currentStep = 0;
|
|
|
|
// Initialize loading state
|
|
$(document).ready(function() {
|
|
showLoadingState();
|
|
animateLoadingText();
|
|
});
|
|
|
|
// Show loading state with smooth animation
|
|
function showLoadingState() {
|
|
$('#loadingState').css('opacity', '1').show();
|
|
$('#wmksContainer').css('opacity', '0').hide();
|
|
}
|
|
|
|
// Hide loading state and show console
|
|
function hideLoadingAndShowConsole() {
|
|
$('#loadingText').text('{{ __('Kết nối thành công!') }}');
|
|
$('#connectionStatus').text('{{ __('Console đã sẵn sàng') }}');
|
|
|
|
setTimeout(() => {
|
|
$('#loadingState').animate({
|
|
opacity: 0
|
|
}, 400, function() {
|
|
$(this).hide();
|
|
$('#wmksContainer').show().animate({
|
|
opacity: 1
|
|
}, 400);
|
|
});
|
|
}, 800);
|
|
}
|
|
|
|
// Show error state
|
|
function showErrorState(errorMessage) {
|
|
$('#loadingState').animate({
|
|
opacity: 0
|
|
}, 300, function() {
|
|
$(this).hide();
|
|
$('#wmksContainer').html(`
|
|
<div class="d-flex align-items-center justify-content-center w-100 h-100">
|
|
<div class="text-center text-black">
|
|
<div class="mb-3">
|
|
<i class="fas fa-exclamation-triangle text-danger" style="font-size: 3rem;"></i>
|
|
</div>
|
|
<h5>{{ __('Kết nối thất bại') }}</h5>
|
|
<p class="text-muted">${errorMessage || '{{ __('Máy chủ không sẵn sàng hoặc có lỗi kết nối') }}'}</p>
|
|
<button onclick="location.reload()" class="btn btn-primary">
|
|
<i class="fas fa-redo"></i> {{ __('Thử lại') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`).show().animate({
|
|
opacity: 1
|
|
}, 400);
|
|
});
|
|
}
|
|
|
|
// Animate loading text through steps
|
|
function animateLoadingText() {
|
|
setInterval(() => {
|
|
if (currentStep < loadingSteps.length - 1) {
|
|
currentStep++;
|
|
$('#loadingText').fadeOut(200, function() {
|
|
$(this).text(loadingSteps[currentStep]).fadeIn(200);
|
|
});
|
|
}
|
|
}, 1500);
|
|
}
|
|
|
|
// Update connection status
|
|
function updateConnectionStatus(status) {
|
|
$('#connectionStatus').fadeOut(200, function() {
|
|
$(this).text(status).fadeIn(200);
|
|
});
|
|
}
|
|
|
|
let WMKSContainer = $('#wmksContainer');
|
|
var wmks = WMKS.createWMKS("wmksContainer", {})
|
|
.register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE, function(event, data) {
|
|
if (data.state === WMKS.CONST.ConnectionState.CONNECTED) {
|
|
console.log("connection state change : connected");
|
|
// Hide loading when WMKS successfully connects
|
|
hideLoadingAndShowConsole();
|
|
}
|
|
});
|
|
|
|
const getTicket = new Promise((resolve, reject) => {
|
|
updateConnectionStatus('{{ __('Đang lấy ticket xác thực...') }}');
|
|
|
|
$.ajax({
|
|
url: '{{ route('api.GetConsoleTicket') }}',
|
|
type: 'get',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer {{ request()->cookie('accessToken') }}'
|
|
},
|
|
data: {
|
|
product: {{ $product->id ?? 0 }}
|
|
},
|
|
success: function(response) {
|
|
if (response.error === false) {
|
|
updateConnectionStatus('{{ __('Đang thiết lập kết nối Console...') }}');
|
|
resolve(response);
|
|
} else {
|
|
reject(response.message);
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
reject(`{{ __('Lỗi kết nối:') }} ${error}`);
|
|
}
|
|
});
|
|
});
|
|
|
|
getTicket
|
|
.then(result => {
|
|
if (result !== false) {
|
|
updateConnectionStatus('{{ __('Đang kết nối đến Console...') }}');
|
|
const wmksHost = '{{ config('vmware.wmks_host', '10.10.10.120') }}';
|
|
wmks.connect(`wss://${wmksHost}/ticket/${result.data}`);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log(`Có lỗi xảy ra: ${error}.`);
|
|
showErrorState(error);
|
|
});
|
|
|
|
// Console control functions
|
|
function enterFullScreen() {
|
|
if (wmks && wmks.canFullScreen()) {
|
|
wmks.enterFullScreen();
|
|
}
|
|
}
|
|
|
|
function sendCAD() {
|
|
if (wmks) {
|
|
wmks.sendCAD();
|
|
}
|
|
}
|
|
|
|
function sendInputString() {
|
|
const inputText = $('#sendStringText').val().trim();
|
|
if (inputText && wmks) {
|
|
wmks.sendInputString(inputText);
|
|
$('#sendStringText').val('');
|
|
} else if (!inputText) {
|
|
alert('{{ __('Vui lòng nhập văn bản cần gửi') }}');
|
|
}
|
|
}
|
|
|
|
// Enter key support for input field
|
|
$('#sendStringText').on('keypress', function(e) {
|
|
if (e.which == 13) {
|
|
sendInputString();
|
|
}
|
|
});
|
|
</script>
|
|
@endsection
|