// 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 → с инстансами сервиса * - boolean → с onblur="validateJson(this)" * - остальное → * * @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 = ``; } else if (p.refSvcId) { // Ссылка на другой сервис → выпадающий список инстансов этого сервиса const refInsts = allInst.filter(i => i.serviceId === p.refSvcId && i.explainedStatus !== 'deleted'); let opts = refInsts.map(i => ``).join(''); if (!dfl) opts = '' + opts; input = ``; } else if (p.dataType === 'boolean') { // Булево значение → выпадающий список true/false input = ``; } else if (p.dataDescriptor && isMap) { // Map с фиксированной структурой → renderMapFixedRow (вложенные поля) return renderMapFixedRow(p, dfl); } else { // Обычное текстовое поле (string, integer, map без структуры) const dt = isMap ? 'map' : ''; input = ``; if (isMap) input += ``; } return `
${input}
`; } /** * Отрисовать 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 = ``; } else if (sub.dataType === 'boolean') { // Булево si = ``; } else { // Текстовое поле si = ``; } subHtml += `
${si}
`; }); // Основной лейбл + вложенные поля с вертикальной чертой return `
${subHtml}
`; } /** * Собрать значения ВСЕХ полей формы → {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; }