Подробные комментарии ко всем JS-файлам фронтенда
This commit is contained in:
@@ -1,70 +1,141 @@
|
||||
// params-render.js — общий рендер параметров операций
|
||||
// Используется: operations.js (ручной режим), scenario-form.js (редактор сценариев)
|
||||
// params-render.js — общий рендер параметров операций (формы ввода)
|
||||
//
|
||||
// Используется в двух местах:
|
||||
// 1. operations.js — ручной режим (форма параметров для modify/create)
|
||||
// 2. scenario-form.js — редактор сценариев (параметры каждого шага)
|
||||
//
|
||||
// Зависимости: _esc() из utils.js, validateJson() из utils.js
|
||||
//
|
||||
// Экспортирует (в глобальную область):
|
||||
// renderParamRow(p, allInst) — отрисовать ОДНУ строку параметра
|
||||
// renderMapFixedRow(p, dfl) — отрисовать map-параметр с фиксированными ключами
|
||||
// collectParams(containerSelector)— собрать значения из формы → {numeric_id: value}
|
||||
|
||||
/**
|
||||
* Отрисовать строку параметра: лейбл + поле ввода (input/select).
|
||||
*
|
||||
* Типы полей:
|
||||
* - valueList → <select> с вариантами
|
||||
* - refSvcId → <select> с инстансами сервиса
|
||||
* - boolean → <select> true/false
|
||||
* - map + dataDescriptor → renderMapFixedRow (вложенные поля)
|
||||
* - map без dataDescriptor → <input> с onblur="validateJson(this)"
|
||||
* - остальное → <input type="text">
|
||||
*
|
||||
* @param {Object} p — параметр из API:
|
||||
* {svcOperationCfsParamId, name, dataType, isRequired, defaultValue, valueList, refSvcId, dataDescriptor}
|
||||
* @param {Array} allInst — все инстансы (нужно для refSvcId-селектов)
|
||||
* @returns {string} HTML-строка
|
||||
*/
|
||||
function renderParamRow(p, allInst) {
|
||||
const req = p.isRequired;
|
||||
const hasDfl = p.defaultValue !== null && p.defaultValue !== undefined && p.defaultValue !== '';
|
||||
// CSS-класс лейбла: req (обязательный+заполнен), req-nodfl (обязательный+пустой)
|
||||
const labelCls = req ? (hasDfl ? 'req' : 'req-nodfl') : '';
|
||||
const dfl = hasDfl ? p.defaultValue : (req ? '' : '');
|
||||
const vl = p.valueList;
|
||||
const isMap = (p.dataType || '').startsWith('map');
|
||||
|
||||
let input;
|
||||
|
||||
if (vl && Array.isArray(vl)) {
|
||||
// Выпадающий список с фиксированными значениями
|
||||
input = `<select name="p_${p.svcOperationCfsParamId}" data-type="${_esc(p.dataType)}">${vl.map(v => `<option value="${_esc(v)}" ${v == dfl ? 'selected' : ''}>${_esc(v)}</option>`).join('')}</select>`;
|
||||
|
||||
} else if (p.refSvcId) {
|
||||
// Ссылка на другой сервис → выпадающий список инстансов этого сервиса
|
||||
const refInsts = allInst.filter(i => i.serviceId === p.refSvcId && i.explainedStatus !== 'deleted');
|
||||
let opts = refInsts.map(i => `<option value="${_esc(i.instanceUid)}" ${i.instanceUid === dfl ? 'selected' : ''}>${_esc(i.displayName)}</option>`).join('');
|
||||
if (!dfl) opts = '<option value="">— выбрать —</option>' + opts;
|
||||
input = `<select name="p_${p.svcOperationCfsParamId}" data-type="ref">${opts}</select>`;
|
||||
|
||||
} else if (p.dataType === 'boolean') {
|
||||
// Булево значение → выпадающий список true/false
|
||||
input = `<select name="p_${p.svcOperationCfsParamId}" data-type="boolean"><option value="true" ${dfl === 'true' || dfl === true ? 'selected' : ''}>true</option><option value="false" ${dfl === 'false' || dfl === false ? 'selected' : ''}>false</option></select>`;
|
||||
|
||||
} else if (p.dataDescriptor && isMap) {
|
||||
// Map с фиксированной структурой → renderMapFixedRow (вложенные поля)
|
||||
return renderMapFixedRow(p, dfl);
|
||||
|
||||
} else {
|
||||
// Обычное текстовое поле (string, integer, map без структуры)
|
||||
const dt = isMap ? 'map' : '';
|
||||
input = `<input type="text" name="p_${p.svcOperationCfsParamId}" value="${_esc(dfl)}" data-type="${dt}" placeholder="${req && !hasDfl ? 'обязательно' : ''}"${isMap ? ' onblur="validateJson(this)"' : ''}>`;
|
||||
if (isMap) input += `<span class="json-err" style="display:none;color:var(--destructive);font-size:10px;margin-left:4px;"></span>`;
|
||||
}
|
||||
|
||||
return `<div class="param-row"><label class="${labelCls}">${_esc(p.name)}</label>${input}</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отрисовать map-параметр с ФИКСИРОВАННЫМИ ключами (dataDescriptor).
|
||||
*
|
||||
* Каждый ключ dataDescriptor → отдельная строка с под-лейблом.
|
||||
* Вложенные поля отбиваются вертикальной чертой слева.
|
||||
*
|
||||
* @param {Object} p — параметр с dataDescriptor
|
||||
* @param {string} dfl — текущее значение (JSON-строка)
|
||||
* @returns {string} HTML
|
||||
*/
|
||||
function renderMapFixedRow(p, dfl) {
|
||||
const dd = p.dataDescriptor;
|
||||
const labelCls = p.isRequired ? (dfl ? 'req' : 'req-nodfl') : '';
|
||||
let subHtml = '';
|
||||
// Парсим текущее значение (JSON → объект)
|
||||
let dflObj = {};
|
||||
try { dflObj = JSON.parse(dfl || '{}'); } catch (e) { }
|
||||
|
||||
Object.keys(dd).forEach(subKey => {
|
||||
const sub = dd[subKey];
|
||||
// Значение sub-ключа: из parsed JSON → default → пусто
|
||||
const subDfl = dflObj[subKey] !== undefined ? dflObj[subKey] : (sub.defaultValue || '');
|
||||
const subVl = sub.valueList;
|
||||
const subReq = sub.isRequired;
|
||||
const subNoDfl = subReq && !subDfl && subDfl !== 0 && subDfl !== false;
|
||||
const subLblCls = subNoDfl ? 'req-nodfl' : '';
|
||||
let si;
|
||||
|
||||
if (subVl && Array.isArray(subVl)) {
|
||||
// Выпадающий список
|
||||
si = `<select name="p_${p.svcOperationCfsParamId}.${subKey}" data-type="mapchild">${subVl.map(v => `<option value="${_esc(v)}" ${v == subDfl ? 'selected' : ''}>${_esc(v)}</option>`).join('')}</select>`;
|
||||
} else if (sub.dataType === 'boolean') {
|
||||
// Булево
|
||||
si = `<select name="p_${p.svcOperationCfsParamId}.${subKey}" data-type="mapchild"><option value="true" ${subDfl === 'true' || subDfl === true ? 'selected' : ''}>true</option><option value="false" ${subDfl === 'false' || subDfl === false ? 'selected' : ''}>false</option></select>`;
|
||||
} else {
|
||||
// Текстовое поле
|
||||
si = `<input type="text" name="p_${p.svcOperationCfsParamId}.${subKey}" value="${_esc(subDfl)}" data-type="mapchild">`;
|
||||
}
|
||||
subHtml += `<div class="param-row"><label class="${subLblCls}">${_esc(subKey)}</label>${si}</div>`;
|
||||
});
|
||||
|
||||
// Основной лейбл + вложенные поля с вертикальной чертой
|
||||
return `<div class="param-row"><label class="${labelCls}">${_esc(p.name)}</label></div><div style="border-left:2px solid var(--brand-gray);margin-left:8px;padding:0 0 4px 8px;">${subHtml}</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Собрать значения ВСЕХ полей формы → {numeric_id: string_value}.
|
||||
*
|
||||
* Для mapchild-полей (с точкой в имени) — собирает вложенный объект
|
||||
* и сериализует в JSON.
|
||||
*
|
||||
* @param {string} containerSelector — CSS-селектор контейнера (по умолчанию '#params-form')
|
||||
* @returns {Object} {paramId: value, ...}
|
||||
*/
|
||||
function collectParams(containerSelector) {
|
||||
containerSelector = containerSelector || '#params-form';
|
||||
const pp = {};
|
||||
const nested = {};
|
||||
const pp = {}; // результат: {numeric_id: value}
|
||||
const nested = {}; // временно: {parentId: {subKey: value}} для mapchild
|
||||
const container = document.querySelector(containerSelector);
|
||||
if (!container) return pp;
|
||||
|
||||
// Перебираем ВСЕ элементы с name^="p_"
|
||||
container.querySelectorAll('[name^="p_"]').forEach(el => {
|
||||
let v = el.value;
|
||||
const dt = el.dataset.type || '';
|
||||
|
||||
if (dt === 'mapchild') {
|
||||
// Вложенный параметр: имя вида "p_198.host"
|
||||
// Извлекаем parentId="198", subKey="host"
|
||||
const name = el.name.replace('p_', '');
|
||||
const dot = name.indexOf('.');
|
||||
const parentKey = name.substring(0, dot);
|
||||
@@ -73,10 +144,17 @@ function collectParams(containerSelector) {
|
||||
nested[parentKey][subKey] = v;
|
||||
return;
|
||||
}
|
||||
|
||||
// Тип array: оборачиваем в JSON-массив
|
||||
if (dt === 'array' || dt.startsWith('array')) v = JSON.stringify([v]);
|
||||
// Тип map: пустое значение → '{}'
|
||||
if (dt === 'map' || dt === 'map-fixed') v = v || '{}';
|
||||
|
||||
pp[el.name.replace('p_', '')] = v;
|
||||
});
|
||||
|
||||
// Сериализовать вложенные mapchild в JSON-строки
|
||||
for (const pk in nested) pp[pk] = JSON.stringify(nested[pk]);
|
||||
|
||||
return pp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user