968 lines
27 KiB
JavaScript
968 lines
27 KiB
JavaScript
class _ThreadManager {
|
|
constructor() {
|
|
this.processes = [];
|
|
this.currentProcess = null;
|
|
this.elements = {
|
|
processContainer: document.getElementById('processContainer'),
|
|
editMask: document.getElementById('editMask'),
|
|
editSidebar: document.getElementById('editSidebar'),
|
|
editFormContainer: document.getElementById('editFormContainer'),
|
|
closeEditBtn: document.getElementById('closeEditBtn'),
|
|
cancelEditBtn: document.getElementById('cancelEditBtn'),
|
|
submitEditBtn: document.getElementById('submitEditBtn'),
|
|
refreshBtn: document.getElementById('refreshBtn')
|
|
};
|
|
this.initEvents();
|
|
}
|
|
|
|
initEvents() {
|
|
this.elements.closeEditBtn.addEventListener('click', () => this.closeEditModal());
|
|
this.elements.cancelEditBtn.addEventListener('click', () => this.closeEditModal());
|
|
this.elements.submitEditBtn.addEventListener('click', () => this.submitEditForm());
|
|
this.elements.refreshBtn.addEventListener('click', () => this.refreshData());
|
|
this.elements.editSidebar.addEventListener('click', (e) => e.stopPropagation());
|
|
this.elements.editMask.addEventListener('click', () => this.closeEditModal());
|
|
}
|
|
|
|
UpdateProcess(processInfo) {
|
|
this.processes = JSON.parse(JSON.stringify(processInfo));
|
|
this.renderProcesses();
|
|
}
|
|
|
|
renderProcesses() {
|
|
const container = this.elements.processContainer;
|
|
container.innerHTML = '';
|
|
|
|
this.processes.forEach(process => {
|
|
const card = document.createElement('div');
|
|
card.className = 'bg-dark-light rounded-lg border border-border-gray overflow-hidden card-hover';
|
|
const status = process.status || "运行中";
|
|
const statusClass = process.statusClass || "bg-primary/20 text-primary";
|
|
|
|
card.innerHTML = `
|
|
<div class="p-3 border-b border-border-gray flex items-center justify-between">
|
|
<h3 class="font-medium">${process.name}</h3>
|
|
<span class="text-xs ${statusClass} px-2 py-0.5 rounded">${status}</span>
|
|
</div>
|
|
<div class="p-3">
|
|
<div class="flex items-center justify-between text-sm mb-2">
|
|
<span class="text-gray-400">简介</span>
|
|
<span>${process.description}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm mb-2">
|
|
<span class="text-gray-400">进度</span>
|
|
<div class="flex items-center gap-2 flex-1 max-w-[180px] justify-end">
|
|
<div class="w-full bg-console-bg rounded-full h-2">
|
|
<div class="bg-primary h-2 rounded-full" style="width: ${process.progress}%"></div>
|
|
</div>
|
|
<span class="text-xs text-gray-300 w-8 text-right">${process.progress}%</span>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3 flex gap-2">
|
|
<button class="edit-btn btn-outline text-xs py-1 px-2 flex-1">
|
|
<i class="fa fa-pencil mr-1"></i> 编辑
|
|
</button>
|
|
<button class="terminate-btn btn-outline text-xs py-1 px-2 flex-1">
|
|
<i class="fa fa-stop mr-1"></i> 终止
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(card);
|
|
card.querySelector('.edit-btn').addEventListener('click', () => this.openEditModal(process));
|
|
card.querySelector('.terminate-btn').addEventListener('click', () => this.terminateProcess(process));
|
|
});
|
|
}
|
|
|
|
openEditModal(process) {
|
|
this.currentProcess = process;
|
|
this.renderEditForm(process);
|
|
this.elements.editMask.classList.remove('opacity-0', 'pointer-events-none');
|
|
this.elements.editMask.classList.add('mask-enter');
|
|
this.elements.editSidebar.classList.remove('translate-x-full');
|
|
this.elements.editSidebar.classList.add('sidebar-enter');
|
|
}
|
|
|
|
closeEditModal() {
|
|
this.elements.editMask.classList.add('mask-leave');
|
|
this.elements.editSidebar.classList.add('sidebar-leave');
|
|
setTimeout(() => {
|
|
this.elements.editMask.classList.add('opacity-0', 'pointer-events-none');
|
|
this.elements.editMask.classList.remove('mask-enter', 'mask-leave');
|
|
this.elements.editSidebar.classList.add('translate-x-full');
|
|
this.elements.editSidebar.classList.remove('sidebar-enter', 'sidebar-leave');
|
|
}, 300);
|
|
}
|
|
|
|
// 渲染编辑表单(新增JSON类型支持)
|
|
renderEditForm(process) {
|
|
const formContainer = this.elements.editFormContainer;
|
|
formContainer.innerHTML = '';
|
|
|
|
// 进程信息标题
|
|
const infoDiv = document.createElement('div');
|
|
infoDiv.className = 'mb-6';
|
|
infoDiv.innerHTML = `
|
|
<h4 class="text-sm text-gray-400 mb-1">进程</h4>
|
|
<p class="font-medium">${process.name}</p>
|
|
<div class="mt-2 flex items-center gap-2">
|
|
<span class="text-xs bg-primary/20 text-primary px-2 py-0.5 rounded">运行中</span>
|
|
<span class="text-xs text-gray-400">${process.description}</span>
|
|
</div>
|
|
`;
|
|
formContainer.appendChild(infoDiv);
|
|
|
|
// 分割线
|
|
const hr = document.createElement('hr');
|
|
hr.className = 'border-border-gray my-4';
|
|
formContainer.appendChild(hr);
|
|
|
|
// 渲染字段
|
|
process.editor.forEach(field => {
|
|
const fieldContainer = document.createElement('div');
|
|
fieldContainer.className = 'mb-4';
|
|
let fieldHtml = '';
|
|
|
|
switch (field.type) {
|
|
case 'int':
|
|
case 'float':
|
|
case 'str':
|
|
fieldHtml = `
|
|
<label for="${field.key}" class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<input
|
|
type="${field.type === 'int' ? 'number' : field.type === 'float' ? 'number' : 'text'}"
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-full bg-console-bg border border-border-gray rounded px-3 py-2 text-gray-200 focus:outline-none focus:ring-1 focus:ring-primary"
|
|
value="${process.default[field.key]}"
|
|
${field.type === 'int' ? 'step="1"' : field.type === 'float' ? 'step="0.01"' : ''}
|
|
>
|
|
`;
|
|
break;
|
|
|
|
case 'bool':
|
|
fieldHtml = `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] === true) ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}" class="ml-2 text-sm font-medium text-gray-300">${field.name}</label>
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
case 'radio':
|
|
fieldHtml = `
|
|
<label class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<div class="space-y-2">
|
|
${field.choices.map(choice => `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="radio"
|
|
id="${field.key}_${choice}"
|
|
name="${field.key}"
|
|
value="${choice}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] || '') === choice ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}_${choice}" class="ml-2 text-sm text-gray-300">${choice}</label>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
case 'multiple':
|
|
fieldHtml = `
|
|
<label class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<div class="space-y-2">
|
|
${field.choices.map(choice => `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
id="${field.key}_${choice}"
|
|
name="${field.key}"
|
|
value="${choice}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] || []).includes(choice) ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}_${choice}" class="ml-2 text-sm text-gray-300">${choice}</label>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
// 新增JSON类型处理
|
|
case 'json':
|
|
// 将JSON对象转换为格式化的字符串以便编辑
|
|
const jsonValue = process.default[field.key]
|
|
? JSON.stringify(process.default[field.key], null, 2)
|
|
: '{}';
|
|
fieldHtml = `
|
|
<label for="${field.key}" class="block text-sm font-medium text-gray-300 mb-1">
|
|
${field.name} <span class="text-gray-500 text-xs">(JSON格式)</span>
|
|
</label>
|
|
<textarea
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-full bg-console-bg border border-border-gray rounded px-3 py-2 text-gray-200 focus:outline-none focus:ring-1 focus:ring-primary min-h-[120px]"
|
|
placeholder="请输入有效的JSON格式内容"
|
|
>${jsonValue}</textarea>
|
|
<p class="mt-1 text-xs text-gray-500">提示: 请确保JSON格式正确,支持对象和数组</p>
|
|
`;
|
|
break;
|
|
}
|
|
|
|
fieldContainer.innerHTML = fieldHtml;
|
|
formContainer.appendChild(fieldContainer);
|
|
});
|
|
}
|
|
|
|
// 收集表单数据(新增JSON类型处理及错误验证)
|
|
collectFormData() {
|
|
if (!this.currentProcess) return null;
|
|
|
|
const formData = {};
|
|
|
|
for (const field of this.currentProcess.editor) {
|
|
switch (field.type) {
|
|
case 'int':
|
|
case 'float':
|
|
case 'str':
|
|
const input = document.getElementById(field.key);
|
|
if (input) {
|
|
let value = input.value;
|
|
if (field.type === 'int') {
|
|
value = parseInt(value, 10);
|
|
} else if (field.type === 'float') {
|
|
value = parseFloat(value);
|
|
}
|
|
formData[field.key] = value;
|
|
}
|
|
break;
|
|
|
|
case 'bool':
|
|
const checkbox = document.getElementById(field.key);
|
|
if (checkbox) {
|
|
formData[field.key] = checkbox.checked;
|
|
}
|
|
break;
|
|
|
|
case 'radio':
|
|
const radio = document.querySelector(`input[name="${field.key}"]:checked`);
|
|
if (radio) {
|
|
formData[field.key] = radio.value;
|
|
}
|
|
break;
|
|
|
|
case 'multiple':
|
|
const checkboxes = document.querySelectorAll(`input[name="${field.key}"]:checked`);
|
|
formData[field.key] = Array.from(checkboxes).map(cb => cb.value);
|
|
break;
|
|
|
|
// 新增JSON类型数据收集及验证
|
|
case 'json':
|
|
const textarea = document.getElementById(field.key);
|
|
if (textarea) {
|
|
const jsonStr = textarea.value.trim();
|
|
if (!jsonStr) {
|
|
this.showNotification(`[${field.name}] JSON内容不能为空`, 'danger');
|
|
return null;
|
|
}
|
|
try {
|
|
// 尝试解析JSON
|
|
const jsonObj = JSON.parse(jsonStr);
|
|
formData[field.key] = jsonObj;
|
|
} catch (e) {
|
|
// 解析失败提示错误
|
|
this.showNotification(`[${field.name}] JSON格式错误: ${e.message}`, 'danger');
|
|
return null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return formData;
|
|
}
|
|
|
|
submitEditForm() {
|
|
if (!this.currentProcess) return;
|
|
|
|
const formData = this.collectFormData();
|
|
if (!formData) return;
|
|
|
|
socket.emit('T4CMDCommand', {'PCMachineGUID': PCMachineGUID, 'work': 'ProcessRevise', "Command": {"TaskName": this.currentProcess.name, "FormData": formData}})
|
|
|
|
this.refreshData();
|
|
this.renderProcesses();
|
|
this.closeEditModal();
|
|
this.showNotification('编辑成功', 'success');
|
|
}
|
|
|
|
terminateProcess(process) {
|
|
this.processes = this.processes.filter(p => p.name !== process.name);
|
|
socket.emit('T4CMDCommand', {"PCMachineGUID": PCMachineGUID, "work": "ThreadKill", "Command": process.name});
|
|
this.showNotification(`已发送终止进程: ${process.name}`, 'danger');
|
|
this.renderProcesses();
|
|
}
|
|
|
|
showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg transform transition-all duration-500 ease-in-out translate-y-20 opacity-0 z-50`;
|
|
|
|
switch (type) {
|
|
case 'success':
|
|
notification.classList.add('bg-success/20', 'text-success', 'border', 'border-success/40');
|
|
break;
|
|
case 'danger':
|
|
notification.classList.add('bg-danger/20', 'text-danger', 'border', 'border-danger/40');
|
|
break;
|
|
case 'warning':
|
|
notification.classList.add('bg-warning/20', 'text-warning', 'border', 'border-warning/40');
|
|
break;
|
|
default:
|
|
notification.classList.add('bg-info/20', 'text-info', 'border', 'border-info/40');
|
|
}
|
|
|
|
notification.innerHTML = `
|
|
<div class="flex items-center gap-2">
|
|
<i class="fa ${type === 'success' ? 'fa-check-circle' : type === 'danger' ? 'fa-exclamation-circle' : type === 'warning' ? 'fa-exclamation-triangle' : 'fa-info-circle'}"></i>
|
|
<span>${message}</span>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
setTimeout(() => notification.classList.remove('translate-y-20', 'opacity-0'), 10);
|
|
setTimeout(() => {
|
|
notification.classList.add('translate-y-20', 'opacity-0');
|
|
setTimeout(() => document.body.removeChild(notification), 500);
|
|
}, 3000);
|
|
}
|
|
|
|
refreshData() {
|
|
const refreshBtn = this.elements.refreshBtn;
|
|
const icon = refreshBtn.querySelector('i');
|
|
icon.classList.add('refresh-rotate');
|
|
socket.emit('T4CMDCommand', {'PCMachineGUID': PCMachineGUID, 'work': 'ThreadINFO'});
|
|
}
|
|
}
|
|
|
|
const ThreadManager = new _ThreadManager();
|
|
window.ThreadManager = ThreadManager;
|
|
|
|
// 初始化示例数据(本地测试用)
|
|
// ThreadManager.UpdateProcess(sampleProcesses);
|
|
|
|
socket.on('CMDRThread', (message) => {
|
|
console.log(message);
|
|
window.ThreadManager.UpdateProcess(message.process);
|
|
const icon = document.getElementById('refreshBtn').querySelector('i');
|
|
icon.classList.remove('refresh-rotate');
|
|
window.ThreadManager.showNotification('数据刷新成功', 'success');
|
|
});
|
|
|
|
document.getElementById('update-task').addEventListener('click', function() {
|
|
value = document.getElementById("update-url").value;
|
|
if (value == "") {
|
|
window.ThreadManager.showNotification('请输入URL', 'danger');
|
|
return;
|
|
}
|
|
socket.emit('T4CMDCommand', {"PCMachineGUID": PCMachineGUID, "work": "Update", "Command": value});
|
|
if (value) {
|
|
document.getElementById("update-url").value = "";
|
|
}
|
|
const refreshBtn = document.getElementById('refreshBtn');
|
|
const icon = refreshBtn.querySelector('i');
|
|
icon.classList.add('refresh-rotate');
|
|
socket.emit('T4CMDCommand', {'PCMachineGUID': PCMachineGUID, 'work': 'ThreadINFO'});
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _ProcessManager {
|
|
constructor() {
|
|
this.processes = [];
|
|
this.currentProcess = null;
|
|
this.elements = {
|
|
processContainer: document.getElementById('P_processContainer'),
|
|
editMask: document.getElementById('P_editMask'),
|
|
editSidebar: document.getElementById('P_editSidebar'),
|
|
editFormContainer: document.getElementById('P_editFormContainer'),
|
|
closeEditBtn: document.getElementById('P_closeEditBtn'),
|
|
cancelEditBtn: document.getElementById('P_cancelEditBtn'),
|
|
submitEditBtn: document.getElementById('P_submitEditBtn'),
|
|
refreshBtn: document.getElementById('P_refreshBtn')
|
|
};
|
|
this.initEvents();
|
|
}
|
|
|
|
initEvents() {
|
|
this.elements.closeEditBtn.addEventListener('click', () => this.closeEditModal());
|
|
this.elements.cancelEditBtn.addEventListener('click', () => this.closeEditModal());
|
|
this.elements.submitEditBtn.addEventListener('click', () => this.submitEditForm());
|
|
this.elements.refreshBtn.addEventListener('click', () => this.refreshData());
|
|
this.elements.editSidebar.addEventListener('click', (e) => e.stopPropagation());
|
|
this.elements.editMask.addEventListener('click', () => this.closeEditModal());
|
|
}
|
|
|
|
UpdateProcess(processInfo) {
|
|
this.processes = JSON.parse(JSON.stringify(processInfo));
|
|
this.renderProcesses();
|
|
}
|
|
|
|
renderProcesses() {
|
|
const container = this.elements.processContainer;
|
|
container.innerHTML = '';
|
|
|
|
this.processes.forEach(process => {
|
|
const card = document.createElement('div');
|
|
card.className = 'bg-dark-light rounded-lg border border-border-gray overflow-hidden card-hover';
|
|
const status = process.status || "运行中";
|
|
const statusClass = process.statusClass || "bg-primary/20 text-primary";
|
|
|
|
card.innerHTML = `
|
|
<div class="p-3 border-b border-border-gray flex items-center justify-between">
|
|
<h3 class="font-medium">${process.name}</h3>
|
|
<span class="text-xs ${statusClass} px-2 py-0.5 rounded">${status}</span>
|
|
</div>
|
|
<div class="p-3">
|
|
<div class="flex items-center justify-between text-sm mb-2">
|
|
<span class="text-gray-400">简介</span>
|
|
<span>${process.description}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between text-sm mb-2">
|
|
<span class="text-gray-400">进度</span>
|
|
<div class="flex items-center gap-2 flex-1 max-w-[180px] justify-end">
|
|
<div class="w-full bg-console-bg rounded-full h-2">
|
|
<div class="bg-primary h-2 rounded-full" style="width: ${process.progress}%"></div>
|
|
</div>
|
|
<span class="text-xs text-gray-300 w-8 text-right">${process.progress}%</span>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3 flex gap-2">
|
|
<button class="edit-btn btn-outline text-xs py-1 px-2 flex-1">
|
|
<i class="fa fa-pencil mr-1"></i> 编辑
|
|
</button>
|
|
<button class="terminate-btn btn-outline text-xs py-1 px-2 flex-1">
|
|
<i class="fa fa-stop mr-1"></i> 终止
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(card);
|
|
card.querySelector('.edit-btn').addEventListener('click', () => this.openEditModal(process));
|
|
card.querySelector('.terminate-btn').addEventListener('click', () => this.terminateProcess(process));
|
|
});
|
|
}
|
|
|
|
openEditModal(process) {
|
|
this.currentProcess = process;
|
|
this.renderEditForm(process);
|
|
this.elements.editMask.classList.remove('opacity-0', 'pointer-events-none');
|
|
this.elements.editMask.classList.add('mask-enter');
|
|
this.elements.editSidebar.classList.remove('translate-x-full');
|
|
this.elements.editSidebar.classList.add('sidebar-enter');
|
|
}
|
|
|
|
closeEditModal() {
|
|
this.elements.editMask.classList.add('mask-leave');
|
|
this.elements.editSidebar.classList.add('sidebar-leave');
|
|
setTimeout(() => {
|
|
this.elements.editMask.classList.add('opacity-0', 'pointer-events-none');
|
|
this.elements.editMask.classList.remove('mask-enter', 'mask-leave');
|
|
this.elements.editSidebar.classList.add('translate-x-full');
|
|
this.elements.editSidebar.classList.remove('sidebar-enter', 'sidebar-leave');
|
|
}, 300);
|
|
}
|
|
|
|
// 渲染编辑表单(新增JSON类型支持)
|
|
renderEditForm(process) {
|
|
const formContainer = this.elements.editFormContainer;
|
|
formContainer.innerHTML = '';
|
|
|
|
// 进程信息标题
|
|
const infoDiv = document.createElement('div');
|
|
infoDiv.className = 'mb-6';
|
|
infoDiv.innerHTML = `
|
|
<h4 class="text-sm text-gray-400 mb-1">进程</h4>
|
|
<p class="font-medium">${process.name}</p>
|
|
<div class="mt-2 flex items-center gap-2">
|
|
<span class="text-xs bg-primary/20 text-primary px-2 py-0.5 rounded">运行中</span>
|
|
<span class="text-xs text-gray-400">${process.description}</span>
|
|
</div>
|
|
`;
|
|
formContainer.appendChild(infoDiv);
|
|
|
|
// 分割线
|
|
const hr = document.createElement('hr');
|
|
hr.className = 'border-border-gray my-4';
|
|
formContainer.appendChild(hr);
|
|
|
|
// 渲染字段
|
|
process.editor.forEach(field => {
|
|
const fieldContainer = document.createElement('div');
|
|
fieldContainer.className = 'mb-4';
|
|
let fieldHtml = '';
|
|
|
|
switch (field.type) {
|
|
case 'int':
|
|
case 'float':
|
|
case 'str':
|
|
fieldHtml = `
|
|
<label for="${field.key}" class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<input
|
|
type="${field.type === 'int' ? 'number' : field.type === 'float' ? 'number' : 'text'}"
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-full bg-console-bg border border-border-gray rounded px-3 py-2 text-gray-200 focus:outline-none focus:ring-1 focus:ring-primary"
|
|
value="${process.default[field.key]}"
|
|
${field.type === 'int' ? 'step="1"' : field.type === 'float' ? 'step="0.01"' : ''}
|
|
>
|
|
`;
|
|
break;
|
|
|
|
case 'bool':
|
|
fieldHtml = `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] === true) ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}" class="ml-2 text-sm font-medium text-gray-300">${field.name}</label>
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
case 'radio':
|
|
fieldHtml = `
|
|
<label class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<div class="space-y-2">
|
|
${field.choices.map(choice => `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="radio"
|
|
id="${field.key}_${choice}"
|
|
name="${field.key}"
|
|
value="${choice}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] || '') === choice ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}_${choice}" class="ml-2 text-sm text-gray-300">${choice}</label>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
case 'multiple':
|
|
fieldHtml = `
|
|
<label class="block text-sm font-medium text-gray-300 mb-1">${field.name}</label>
|
|
<div class="space-y-2">
|
|
${field.choices.map(choice => `
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
id="${field.key}_${choice}"
|
|
name="${field.key}"
|
|
value="${choice}"
|
|
class="w-4 h-4 text-primary focus:ring-primary border-border-gray bg-console-bg"
|
|
${(process.default[field.key] || []).includes(choice) ? 'checked' : ''}
|
|
>
|
|
<label for="${field.key}_${choice}" class="ml-2 text-sm text-gray-300">${choice}</label>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
break;
|
|
|
|
// 新增JSON类型处理
|
|
case 'json':
|
|
// 将JSON对象转换为格式化的字符串以便编辑
|
|
const jsonValue = process.default[field.key]
|
|
? JSON.stringify(process.default[field.key], null, 2)
|
|
: '{}';
|
|
fieldHtml = `
|
|
<label for="${field.key}" class="block text-sm font-medium text-gray-300 mb-1">
|
|
${field.name} <span class="text-gray-500 text-xs">(JSON格式)</span>
|
|
</label>
|
|
<textarea
|
|
id="${field.key}"
|
|
name="${field.key}"
|
|
class="w-full bg-console-bg border border-border-gray rounded px-3 py-2 text-gray-200 focus:outline-none focus:ring-1 focus:ring-primary min-h-[120px]"
|
|
placeholder="请输入有效的JSON格式内容"
|
|
>${jsonValue}</textarea>
|
|
<p class="mt-1 text-xs text-gray-500">提示: 请确保JSON格式正确,支持对象和数组</p>
|
|
`;
|
|
break;
|
|
}
|
|
|
|
fieldContainer.innerHTML = fieldHtml;
|
|
formContainer.appendChild(fieldContainer);
|
|
});
|
|
}
|
|
|
|
// 收集表单数据(新增JSON类型处理及错误验证)
|
|
collectFormData() {
|
|
if (!this.currentProcess) return null;
|
|
|
|
const formData = {};
|
|
|
|
for (const field of this.currentProcess.editor) {
|
|
switch (field.type) {
|
|
case 'int':
|
|
case 'float':
|
|
case 'str':
|
|
const input = document.getElementById(field.key);
|
|
if (input) {
|
|
let value = input.value;
|
|
if (field.type === 'int') {
|
|
value = parseInt(value, 10);
|
|
} else if (field.type === 'float') {
|
|
value = parseFloat(value);
|
|
}
|
|
formData[field.key] = value;
|
|
}
|
|
break;
|
|
|
|
case 'bool':
|
|
const checkbox = document.getElementById(field.key);
|
|
if (checkbox) {
|
|
formData[field.key] = checkbox.checked;
|
|
}
|
|
break;
|
|
|
|
case 'radio':
|
|
const radio = document.querySelector(`input[name="${field.key}"]:checked`);
|
|
if (radio) {
|
|
formData[field.key] = radio.value;
|
|
}
|
|
break;
|
|
|
|
case 'multiple':
|
|
const checkboxes = document.querySelectorAll(`input[name="${field.key}"]:checked`);
|
|
formData[field.key] = Array.from(checkboxes).map(cb => cb.value);
|
|
break;
|
|
|
|
// 新增JSON类型数据收集及验证
|
|
case 'json':
|
|
const textarea = document.getElementById(field.key);
|
|
if (textarea) {
|
|
const jsonStr = textarea.value.trim();
|
|
if (!jsonStr) {
|
|
this.showNotification(`[${field.name}] JSON内容不能为空`, 'danger');
|
|
return null;
|
|
}
|
|
try {
|
|
// 尝试解析JSON
|
|
const jsonObj = JSON.parse(jsonStr);
|
|
formData[field.key] = jsonObj;
|
|
} catch (e) {
|
|
// 解析失败提示错误
|
|
this.showNotification(`[${field.name}] JSON格式错误: ${e.message}`, 'danger');
|
|
return null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return formData;
|
|
}
|
|
|
|
submitEditForm() {
|
|
if (!this.currentProcess) return;
|
|
|
|
const formData = this.collectFormData();
|
|
if (!formData) return;
|
|
|
|
socket.emit('T4CMDCommand', {'PCMachineGUID': PCMachineGUID, 'work': 'ProcessRevise', "Command": {"TaskName": this.currentProcess.name, "FormData": formData}})
|
|
|
|
this.refreshData();
|
|
this.renderProcesses();
|
|
this.closeEditModal();
|
|
this.showNotification('编辑成功', 'success');
|
|
}
|
|
|
|
terminateProcess(process) {
|
|
this.processes = this.processes.filter(p => p.name !== process.name);
|
|
socket.emit('T4CMDCommand', {"PCMachineGUID": PCMachineGUID, "work": "ProcessKill", "Command": process.name});
|
|
this.showNotification(`已发送终止进程: ${process.name}`, 'danger');
|
|
this.renderProcesses();
|
|
}
|
|
|
|
showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg transform transition-all duration-500 ease-in-out translate-y-20 opacity-0 z-50`;
|
|
|
|
switch (type) {
|
|
case 'success':
|
|
notification.classList.add('bg-success/20', 'text-success', 'border', 'border-success/40');
|
|
break;
|
|
case 'danger':
|
|
notification.classList.add('bg-danger/20', 'text-danger', 'border', 'border-danger/40');
|
|
break;
|
|
case 'warning':
|
|
notification.classList.add('bg-warning/20', 'text-warning', 'border', 'border-warning/40');
|
|
break;
|
|
default:
|
|
notification.classList.add('bg-info/20', 'text-info', 'border', 'border-info/40');
|
|
}
|
|
|
|
notification.innerHTML = `
|
|
<div class="flex items-center gap-2">
|
|
<i class="fa ${type === 'success' ? 'fa-check-circle' : type === 'danger' ? 'fa-exclamation-circle' : type === 'warning' ? 'fa-exclamation-triangle' : 'fa-info-circle'}"></i>
|
|
<span>${message}</span>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
setTimeout(() => notification.classList.remove('translate-y-20', 'opacity-0'), 10);
|
|
setTimeout(() => {
|
|
notification.classList.add('translate-y-20', 'opacity-0');
|
|
setTimeout(() => document.body.removeChild(notification), 500);
|
|
}, 3000);
|
|
}
|
|
|
|
refreshData() {
|
|
const refreshBtn = this.elements.refreshBtn;
|
|
const icon = refreshBtn.querySelector('i');
|
|
icon.classList.add('refresh-rotate');
|
|
socket.emit('T4CMDCommand', {'PCMachineGUID': PCMachineGUID, 'work': 'ProcessINFO'});
|
|
}
|
|
}
|
|
const ProcessManager = new _ProcessManager();
|
|
window.ProcessManager = ProcessManager;
|
|
|
|
// 初始化示例数据(本地测试用)
|
|
// ProcessManager.UpdateProcess(sampleProcesses);
|
|
|
|
socket.on('CMDRProcess', (message) => {
|
|
window.ProcessManager.UpdateProcess(message.process);
|
|
const icon = document.getElementById('refreshBtn').querySelector('i');
|
|
icon.classList.remove('refresh-rotate');
|
|
window.ProcessManager.showNotification('数据刷新成功', 'success');
|
|
});
|