Files
app-autotest/site/static/js/params-render.js

161 lines
8.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 = {}; // результат: {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);
const subKey = name.substring(dot + 1);
if (!nested[parentKey]) nested[parentKey] = {};
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;
}