automated_task_monitor/monitor/static/js/monitor.js

928 lines
31 KiB
JavaScript
Raw Normal View History

// 在文件开头添加调试日志
console.log('monitor.js 加载完成');
// 全局变量声明
let currentDirectory = null;
let selectedPid = null;
let updateInterval = null;
let cpuChart = null;
let memoryChart = null;
let gpuChart = null;
// 更新监控数据
function updateMonitorData(pid) {
// 使用保存的监控类型
const type = currentMonitorType;
console.log('正在更新数据,类型:', type); // 调试日志
// 构建 URL确保包含 type 参数
const url = new URL(`${window.location.origin}/api/process/${pid}/status/`);
url.searchParams.append('type', type);
console.log('请求 URL:', url.toString()); // 调试日志
fetch(url)
.then(response => response.json())
.then(response => {
if (response.status === 'success') {
const data = response.data;
updateUI(data, type);
}
})
.catch(error => handleError(error, '获取监控数据失败'));
}
// 获取所有监控按钮和停止按钮
const monitorButtons = document.querySelectorAll('[data-type]');
const stopBtn = document.getElementById('stopMonitor');
const pidInput = document.getElementById('pidInput');
// 保存当前监控类型的全局变量
let currentMonitorType = null;
// 开始监控
function startMonitoring(pid) {
console.log('开始监控,类型:', currentMonitorType); // 调试日志
updateMonitorData(pid); // 立即执行一次更新
monitorInterval = setInterval(() => {
updateMonitorData(pid);
}, 60000);
}
// 停止监控
function stopMonitoring() {
if (!currentDirectory) {
console.error('没有正在监控的目录');
showMessage('没有正在监控的目录', 'error');
return;
}
const stopBtn = document.getElementById('stopDirectoryMonitor');
if (!stopBtn) return;
stopBtn.disabled = true;
stopBtn.textContent = '正在停止...';
// 先清理定时器
if (updateInterval) {
clearInterval(updateInterval);
updateInterval = null;
}
const directoryToStop = currentDirectory;
fetch('/monitor/stop-directory-monitor/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({ directory: directoryToStop })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
cleanupMonitoringState();
showMessage('监控已停止', 'success');
} else {
throw new Error(data.message || '停止监控失败');
}
})
.catch(error => {
console.error('停止监控失败:', error);
showMessage(error.message || '停止监控失败', 'error');
stopBtn.disabled = false;
})
.finally(() => {
stopBtn.textContent = '停止监控';
});
}
// 初始化图表
function initCharts() {
console.log('初始化图表');
cpuChart = echarts.init(document.getElementById('cpuChart'));
memoryChart = echarts.init(document.getElementById('memoryChart'));
gpuChart = echarts.init(document.getElementById('gpuChart'));
const baseOption = {
tooltip: {
trigger: 'axis',
formatter: function(params) {
const time = new Date(params[0].value[0]).toLocaleTimeString();
return `${time}<br/>${params[0].seriesName}: ${params[0].value[1].toFixed(2)}%`;
}
},
xAxis: {
type: 'time',
splitLine: { show: false }
},
yAxis: {
type: 'value',
min: 0,
max: 100,
splitLine: { show: true }
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
series: [{
type: 'line',
showSymbol: false,
data: [],
smooth: true,
areaStyle: {
opacity: 0.1
}
}]
};
cpuChart.setOption({
...baseOption,
series: [{
...baseOption.series[0],
name: 'CPU使用率',
itemStyle: { color: '#2196F3' }
}]
});
memoryChart.setOption({
...baseOption,
series: [{
...baseOption.series[0],
name: '内存使用量',
itemStyle: { color: '#4CAF50' }
}]
});
gpuChart.setOption({
...baseOption,
series: [{
...baseOption.series[0],
name: 'GPU使用率',
itemStyle: { color: '#FF5722' }
}]
});
}
// 更新图表数据
function updateCharts(process) {
console.log('更新图表:', process);
const now = new Date();
// 更新CPU图表
if (cpuChart) {
updateChartData(cpuChart, now, process.cpu_usage);
}
// 更新内存图表
if (memoryChart) {
updateChartData(memoryChart, now, process.memory_usage);
}
// 更新GPU图表
if (gpuChart && process.gpu_info) {
updateChartData(gpuChart, now, process.gpu_info.usage);
}
}
// 辅助函数:更新图表数据
function updateChartData(chart, time, value) {
if (!chart) return;
try {
const option = chart.getOption();
const data = option.series[0].data || [];
data.push([time, value]);
// 保持最近60个数据点
if (data.length > 60) {
data.shift();
}
chart.setOption({
series: [{
data: data
}]
});
} catch (error) {
console.error('更新图表数据失败:', error);
}
}
// 修改更新进程表格函数
function updateProcessTable(processes) {
console.log('更新进程表格:', processes); // 调试日志
const table = document.getElementById('processTable');
if (!table) {
console.error('找不到进程表格元素');
return;
}
if (!processes || processes.length === 0) {
table.innerHTML = `
<tr>
<td colspan="7" class="text-center">暂无进程数据</td>
</tr>
`;
return;
}
table.innerHTML = processes.map(proc => `
<tr class="${selectedPid && selectedPid == proc.pid ? 'table-primary' : ''}">
<td>${proc.pid}</td>
<td>${proc.name}</td>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1" style="height: 6px;">
<div class="progress-bar" role="progressbar"
style="width: ${proc.cpu_usage}%;"
aria-valuenow="${proc.cpu_usage}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<span class="ms-2">${proc.cpu_usage.toFixed(1)}%</span>
</div>
</td>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: ${(proc.memory_usage/1000)*100}%;"
aria-valuenow="${proc.memory_usage}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<span class="ms-2">${proc.memory_usage.toFixed(1)} MB</span>
</div>
</td>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1" style="height: 6px;">
<div class="progress-bar bg-warning" role="progressbar"
style="width: ${proc.gpu_info.usage}%;"
aria-valuenow="${proc.gpu_info.usage}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<span class="ms-2">${proc.gpu_info.usage}%</span>
</div>
</td>
<td>${proc.gpu_info.memory.toFixed(1)} MB</td>
<td>
<span class="badge bg-success">运行中</span>
<button class="btn btn-sm btn-outline-primary ms-2"
onclick="selectProcess(${proc.pid})">
查看详情
</button>
</td>
</tr>
`).join('');
}
// 修改选择进程的函数
function selectProcess(pid) {
console.log('选择进程:', pid);
selectedPid = pid.toString(); // 确保转换为字符串
// 更新选择器
const selector = document.getElementById('processSelector');
if (selector) {
selector.value = selectedPid;
}
// 更新表格中的选中状态
const rows = document.querySelectorAll('#processTable tr');
rows.forEach(row => {
if (row.cells && row.cells[0] && row.cells[0].textContent === selectedPid) {
row.classList.add('table-primary');
} else {
row.classList.remove('table-primary');
}
});
// 获取最新数据并更新图表
if (currentDirectory) {
fetch(`/monitor/directory-status/?directory=${encodeURIComponent(currentDirectory)}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success' && data.processes) {
const selectedProcess = data.processes.find(p => p.pid.toString() === selectedPid);
if (selectedProcess) {
// 更新图表
updateCharts(selectedProcess);
// 更新详细信息
updateProcessDetails(selectedProcess);
}
}
})
.catch(error => console.error('获取进程详情失败:', error));
}
}
// 修改进程选择器更新函数
function updateProcessSelector(processes) {
console.log('更新进程选择器,进程列表:', processes); // 调试日志
const selector = document.getElementById('processSelector');
const processInfo = document.getElementById('selectedProcessInfo');
if (!selector) {
console.error('找不到进程选择器元素');
return;
}
// 启用选择器
selector.disabled = false;
// 获取当前的进程列表
const currentPids = processes.map(p => p.pid.toString());
console.log('当前PID列表:', currentPids); // 调试日志
// 如果当前选中的进程不在列表中,清除选择
if (selectedPid && !currentPids.includes(selectedPid.toString())) {
console.log('选中的进程不再存在,清除选择'); // 调试日志
selectedPid = null;
clearCharts();
}
// 移除旧的事件监听器
const newSelector = selector.cloneNode(true);
selector.parentNode.replaceChild(newSelector, selector);
// 更新选择器选项
newSelector.innerHTML = `
<option value="">选择要监控的进程</option>
${processes.map(proc => `
<option value="${proc.pid}" ${proc.pid.toString() === selectedPid ? 'selected' : ''}>
PID: ${proc.pid} - ${proc.name} (CPU: ${proc.cpu_usage.toFixed(2)}%)
</option>
`).join('')}
`;
// 添加新的事件监听器
newSelector.addEventListener('change', function() {
console.log('进程选择变更:', this.value);
selectedPid = this.value;
if (selectedPid) {
const selected = processes.find(p => p.pid === parseInt(selectedPid));
if (selected) {
processInfo.textContent = `监控中: PID ${selected.pid} - ${selected.name}`;
updateProcessDetails(selected); // 更新详细信息
clearCharts();
}
} else {
processInfo.textContent = '未选择进程';
clearCharts();
// 清空详细信息
['basicInfo', 'resourceInfo', 'networkInfo', 'fileInfo'].forEach(id => {
document.getElementById(id).innerHTML = '<p>选择进程查看详细信息</p>';
});
}
});
console.log('进程选择器更新完成'); // 调试日志
}
// 添加清除图表数据函数
function clearCharts() {
const emptyOption = {
series: [{
data: []
}]
};
cpuChart.setOption(emptyOption);
memoryChart.setOption(emptyOption);
gpuChart.setOption(emptyOption);
}
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM加载完成开始初始化');
const startBtn = document.getElementById('startDirectoryMonitor');
const stopBtn = document.getElementById('stopDirectoryMonitor');
if (!startBtn || !stopBtn) {
console.error('找不到监控按钮元素');
return;
}
// 初始化图表
initCharts();
// 开始监控按钮事件
startBtn.addEventListener('click', function() {
console.log('点击了开始监控按钮');
const directory = document.getElementById('directoryInput').value;
const statusDiv = document.getElementById('directoryMonitorStatus');
if (!directory) {
statusDiv.style.display = 'block';
statusDiv.className = 'alert alert-warning';
statusDiv.textContent = '请输入目录路径';
return;
}
// 显示加载状态
startBtn.disabled = true;
startBtn.textContent = '启动中...';
statusDiv.style.display = 'block';
statusDiv.className = 'alert alert-info';
statusDiv.textContent = '正在启动监控...';
console.log('开始监控目录:', directory);
// 清除旧的选择
selectedPid = null;
clearCharts();
fetch('/monitor/scan-directory/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ directory: directory })
})
.then(response => response.json())
.then(data => {
console.log('接口返回:', data);
if (data.status === 'success') {
currentDirectory = directory;
statusDiv.className = 'alert alert-success';
statusDiv.textContent = data.message;
startBtn.disabled = true;
stopBtn.disabled = false;
// 开始定期获取详细信息
if (updateInterval) {
clearInterval(updateInterval);
}
currentDirectory = directory;
getProcessDetails(directory); // 立即获取一次
updateInterval = setInterval(() => getProcessDetails(directory), 5000); // 每5秒更新一次
} else {
statusDiv.className = 'alert alert-danger';
statusDiv.textContent = data.message;
startBtn.disabled = false;
}
})
.catch(error => {
console.error('监控启动失败:', error);
statusDiv.className = 'alert alert-danger';
statusDiv.textContent = '启动监控失败: ' + error.message;
startBtn.disabled = false;
})
.finally(() => {
startBtn.textContent = '开始监控';
});
});
// 停止监控按钮事件
if (stopBtn) {
stopBtn.addEventListener('click', function() {
console.log('停止监控,当前目录:', currentDirectory);
// 检查是否有当前目录
if (!currentDirectory) {
console.error('没有正在监控的目录');
showMessage('没有正在监控的目录', 'error');
return;
}
// 禁用按钮,防止重复点击
stopBtn.disabled = true;
stopBtn.textContent = '正在停止...';
// 先清理定时器,防止继续发送请求
if (updateInterval) {
clearInterval(updateInterval);
updateInterval = null;
}
// 保存当前目录的副本
const directoryToStop = currentDirectory;
// 发送停止监控请求到服务器
fetch('/monitor/stop-directory-monitor/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCSRFToken(),
},
body: JSON.stringify({
directory: directoryToStop,
timestamp: new Date().getTime() // 添加时间戳防止缓存
})
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('停止监控响应:', data);
if (data.status === 'success') {
// 清理前端状态
cleanupMonitoringState();
// 显示成功消息
showMessage('监控已停止', 'success');
} else {
throw new Error(data.message || '停止监控失败');
}
})
.catch(error => {
console.error('停止监控失败:', error);
showMessage(error.message || '停止监控请求失败', 'error');
// 恢复按钮状态
stopBtn.disabled = false;
stopBtn.textContent = '停止监控';
});
});
}
// 初始状态设置
stopBtn.disabled = true; // 初始状态下停止按钮禁用
console.log('初始化完成');
// 为每个监控按钮添加点击事件
monitorButtons.forEach(button => {
button.addEventListener('click', function() {
const pid = pidInput.value.trim();
const type = this.dataset.type; // 从按钮的 data-type 属性获取类型
if (!pid) {
alert('请输入进程ID');
return;
}
// 保存当前选择的监控类型
currentMonitorType = type;
console.log('设置监控类型为:', currentMonitorType);
fetch(`/monitor/start/?pid=${pid}&type=${type}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
startMonitoring(pid);
// 禁用所有监控按钮
monitorButtons.forEach(btn => btn.disabled = true);
stopBtn.disabled = false;
pidInput.disabled = true;
} else {
alert(data.message);
}
})
.catch(error => {
console.error('启动监控失败:', error);
alert('启动监控失败');
});
});
});
// 获取自动检测按钮
const startAutoDetectBtn = document.getElementById('startAutoDetectBtn');
const stopAutoDetectBtn = document.getElementById('stopAutoDetectBtn');
// 获取CSRF Token
function getCSRFToken() {
const name = 'csrftoken';
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
// 添加自动检测开始按钮事件
startAutoDetectBtn.addEventListener('click', function() {
fetch('/auto_detect/', {
method: 'POST',
headers: {
'X-CSRFToken': getCSRFToken(),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('已开始自动检测高资源进程');
startAutoDetectBtn.disabled = true;
stopAutoDetectBtn.disabled = false;
} else {
alert(data.message);
}
})
.catch(error => {
console.error('启动自动检测失败:', error);
alert('启动自动检测失败');
});
});
// 添加自动检测停止按钮事件
stopAutoDetectBtn.addEventListener('click', function() {
fetch('/stop_auto_detect/', {
method: 'POST',
headers: {
'X-CSRFToken': getCSRFToken(),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
alert('已停止自动检测');
startAutoDetectBtn.disabled = false;
stopAutoDetectBtn.disabled = true;
} else {
alert(data.message);
}
})
.catch(error => {
console.error('停止自动检测失败:', error);
alert('停止自动检测失败');
});
});
});
// 更新 UI 的函数
function updateUI(data, type) {
// 根据监控类型更新对应的表格
if (type === 'all' || type === 'cpu') {
document.getElementById('cpuTable').innerHTML = `
<tr><td>使用率</td><td>${data.cpu.usage}</td></tr>
<tr><td>用户态时间</td><td>${data.cpu.user_time}</td></tr>
<tr><td>内核态时间</td><td>${data.cpu.system_time}</td></tr>
<tr><td>CPU核心数</td><td>${data.cpu.cores}</td></tr>
<tr><td>CPU频率</td><td>${data.cpu.frequency}</td></tr>
<tr><td>上下文切换</td><td>${data.cpu.context_switches}</td></tr>
`;
document.getElementById('cpuStatus').className = 'badge bg-success status-badge';
document.getElementById('cpuStatus').textContent = '监控中';
}
if (type === 'all' || type === 'memory') {
document.getElementById('memoryTable').innerHTML = `
<tr><td>物理内存</td><td>${data.memory.physical}</td></tr>
<tr><td>虚拟内存</td><td>${data.memory.virtual}</td></tr>
<tr><td>内存映射</td><td>${data.memory.mappings}</td></tr>
<tr><td>系统内存使用</td><td>${data.memory.system_usage}</td></tr>
<tr><td>交换空间使用</td><td>${data.memory.swap_usage}</td></tr>
`;
document.getElementById('memoryStatus').className = 'badge bg-success status-badge';
document.getElementById('memoryStatus').textContent = '监控中';
}
if (type === 'all' || type === 'gpu') {
document.getElementById('gpuTable').innerHTML = `
<tr><td>使用率</td><td>${data.gpu.usage}</td></tr>
<tr><td>显存使用</td><td>${data.gpu.memory}</td></tr>
`;
document.getElementById('gpuStatus').className = 'badge bg-success status-badge';
document.getElementById('gpuStatus').textContent = '监控中';
}
// 更新最后更新时间
document.getElementById('lastUpdate').textContent =
`最后更新: ${data.timestamp}`;
}
// 添加错误处理函数
function handleError(error, message) {
console.error(message, error);
['cpu', 'gpu', 'memory'].forEach(type => {
const statusElement = document.getElementById(`${type}Status`);
if (statusElement) {
statusElement.className = 'badge bg-danger status-badge';
statusElement.textContent = '错误';
}
});
alert(message);
}
// 窗口大小改变时调整图表大小
window.addEventListener('resize', function() {
if (cpuChart) cpuChart.resize();
if (memoryChart) memoryChart.resize();
if (gpuChart) gpuChart.resize();
});
// 更新进程详细信息的函数
function updateProcessDetails(process) {
if (!process) return;
// 检查元素是否存在
const basicInfo = document.getElementById('basicInfo');
const resourceInfo = document.getElementById('resourceInfo');
if (!basicInfo || !resourceInfo) {
console.error('找不到详情显示元素');
return;
}
try {
// 更新基本信息
basicInfo.innerHTML = `
<dl class="row mb-0">
<dt class="col-sm-4">PID</dt>
<dd class="col-sm-8">${process.pid}</dd>
<dt class="col-sm-4">进程名</dt>
<dd class="col-sm-8">${process.name}</dd>
<dt class="col-sm-4">命令行</dt>
<dd class="col-sm-8"><small class="text-muted">${process.command_line}</small></dd>
<dt class="col-sm-4">工作目录</dt>
<dd class="col-sm-8"><small class="text-muted">${process.working_directory}</small></dd>
<dt class="col-sm-4">创建时间</dt>
<dd class="col-sm-8">${process.create_time}</dd>
</dl>
`;
// 更新资源信息
resourceInfo.innerHTML = `
<dl class="row mb-0">
<dt class="col-sm-4">CPU使用率</dt>
<dd class="col-sm-8">${process.cpu_usage.toFixed(2)}%</dd>
<dt class="col-sm-4">内存使用</dt>
<dd class="col-sm-8">${process.memory_usage.toFixed(2)} MB</dd>
<dt class="col-sm-4">线程数</dt>
<dd class="col-sm-8">${process.threads}</dd>
<dt class="col-sm-4">GPU使用率</dt>
<dd class="col-sm-8">${process.gpu_info.usage}%</dd>
<dt class="col-sm-4">GPU内存</dt>
<dd class="col-sm-8">${process.gpu_info.memory.toFixed(2)} MB</dd>
</dl>
`;
} catch (error) {
console.error('更新进程详情失败:', error);
}
}
// 修改定期更新函数
function startPeriodicUpdate(directory) {
console.log('开始定期更新, 目录:', directory);
if (updateInterval) {
clearInterval(updateInterval);
}
// 立即执行一次更新
getProcessDetails(directory);
// 设置定时更新
updateInterval = setInterval(() => {
getProcessDetails(directory);
}, 5000);
}
// 修改获取进程详情的函数
function getProcessDetails(directory) {
if (!directory || !currentDirectory) { // 添加currentDirectory检查
console.log('没有目录或已停止监控');
return;
}
// 使用 AbortController 来控制fetch请求
if (window.currentFetch) {
window.currentFetch.abort();
}
window.currentFetch = new AbortController();
fetch(`/monitor/directory-status/?directory=${encodeURIComponent(directory)}`, {
signal: window.currentFetch.signal
})
.then(response => response.json())
.then(data => {
if (!currentDirectory) { // 再次检查是否已停止监控
console.log('监控已停止,不更新数据');
return;
}
console.log('收到进程数据:', data);
if (data.status === 'success' && data.processes && data.processes.length > 0) {
updateProcessTable(data.processes);
if (selectedPid) {
const selectedProcess = data.processes.find(p => p.pid.toString() === selectedPid);
if (selectedProcess) {
updateCharts(selectedProcess);
updateProcessDetails(selectedProcess);
}
}
}
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('请求被中止');
} else {
console.error('获取进程详情失败:', error);
}
});
}
// 获取所有正在监控的进程ID
function getAllMonitoredPids() {
const table = document.getElementById('processTable');
if (!table) return [];
const pids = [];
const rows = table.getElementsByTagName('tr');
for (let row of rows) {
const firstCell = row.cells[0];
if (firstCell && !isNaN(firstCell.textContent)) {
pids.push(firstCell.textContent);
}
}
return pids;
}
// 修改清理监控状态的函数
function cleanupMonitoringState() {
console.log('清理监控状态');
// 先取消所有正在进行的请求
if (window.currentFetch) {
window.currentFetch.abort();
window.currentFetch = null;
}
// 清除定时器
if (updateInterval) {
clearInterval(updateInterval);
updateInterval = null;
}
// 清除状态变量(在清理完其他内容后再清除)
const oldDirectory = currentDirectory;
currentDirectory = null;
selectedPid = null;
console.log('已清除的目录:', oldDirectory);
// 清空图表
clearCharts();
// 清空进程列表
const processTable = document.getElementById('processTable');
if (processTable) {
processTable.innerHTML = `
<tr>
<td colspan="8" class="text-center">未开始监控</td>
</tr>
`;
}
// 清空详细信息
const basicInfo = document.getElementById('basicInfo');
const resourceInfo = document.getElementById('resourceInfo');
if (basicInfo) {
basicInfo.innerHTML = '<p class="text-center text-muted">未选择进程</p>';
}
if (resourceInfo) {
resourceInfo.innerHTML = '<p class="text-center text-muted">未选择进程</p>';
}
// 更新按钮状态
const startBtn = document.getElementById('startDirectoryMonitor');
const stopBtn = document.getElementById('stopDirectoryMonitor');
if (startBtn) startBtn.disabled = false;
if (stopBtn) {
stopBtn.disabled = true;
stopBtn.textContent = '停止监控';
}
}
// 添加显示消息的函数
function showMessage(message, type = 'info') {
const statusDiv = document.getElementById('directoryMonitorStatus');
if (statusDiv) {
statusDiv.style.display = 'block';
statusDiv.className = `alert alert-${type}`;
statusDiv.textContent = message;
// 3秒后自动隐藏
setTimeout(() => {
statusDiv.style.display = 'none';
}, 3000);
}
}