feat: condición multi-valor en visibilidad condicional de formularios
Builder: - Picker de valor ahora es <select multiple> — Ctrl+clic para varios - Al re-editar un separador, los valores guardados quedan preseleccionados - aplicarCampo() guarda siempre como condicion.valores (array) - Badge en canvas muestra todos los valores separados por coma form_cliente.php / iniciarCondiciones(): - Soporta tanto .valor (string legacy) como .valores (array nuevo) - Evaluación: activo si CUALQUIER valor de la lista está marcado BD (ya aplicado): - Formulario id=15 actualizado con condiciones en los separadores - Taukit → valor único; Heliprobe → valor único - Glicemia/Prolactina/etc. → valores[] con todos los prolongados - Cortisol AM/PM → valores[] para sección de tomas hora fija Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
21e7c6a836
commit
8bf3614fa0
+5
-6
@@ -1126,21 +1126,20 @@ function iniciarCondiciones(esquema) {
|
||||
|
||||
function evaluar() {
|
||||
condicionales.forEach(sec => {
|
||||
const { campo_id, valor } = sec.condicion;
|
||||
// Leer valor del campo controlador
|
||||
const { campo_id, valor, valores } = sec.condicion;
|
||||
const valoresCond = valores ? valores : (valor ? [valor] : []);
|
||||
const ctrl = document.querySelector(`[data-campo-id="${campo_id}"]`);
|
||||
if (!ctrl) return;
|
||||
|
||||
let activo = false;
|
||||
const checks = ctrl.querySelectorAll('input[type="checkbox"]');
|
||||
if (checks.length) {
|
||||
// checkbox múltiple: activo si alguno con ese valor está checked
|
||||
checks.forEach(cb => { if (cb.value === valor && cb.checked) activo = true; });
|
||||
checks.forEach(cb => { if (valoresCond.includes(cb.value) && cb.checked) activo = true; });
|
||||
} else {
|
||||
const radio = ctrl.querySelector(`input[type="radio"]:checked`);
|
||||
const sel = ctrl.querySelector('select');
|
||||
if (radio) activo = radio.value === valor;
|
||||
else if (sel) activo = sel.value === valor;
|
||||
const v = radio ? radio.value : (sel ? sel.value : '');
|
||||
activo = valoresCond.includes(v);
|
||||
}
|
||||
|
||||
// Mostrar u ocultar toda la sección
|
||||
|
||||
+28
-14
@@ -708,7 +708,7 @@ function renderPreview() {
|
||||
function renderCampoPreview(c) {
|
||||
if (c.tipo === 'separador') {
|
||||
const condBadge = c.condicion
|
||||
? `<span title="Visible solo si: ${esc(c.condicion.valor)}" style="font-size:9px;background:#fff3cd;color:#856404;border-radius:4px;padding:1px 5px;margin-left:6px;vertical-align:middle"><i class="fas fa-eye-slash"></i> si: ${esc(c.condicion.valor)}</span>`
|
||||
? (()=>{ const vs=(c.condicion.valores||[c.condicion.valor]).join(', '); return `<span title="Visible si: ${esc(vs)}" style="font-size:9px;background:#fff3cd;color:#856404;border-radius:4px;padding:1px 5px;margin-left:6px;vertical-align:middle"><i class="fas fa-eye-slash"></i> si: ${esc(vs)}</span>`; })()
|
||||
: '';
|
||||
return `<hr class="preview-sep"><div class="preview-sep-label">${esc(c.label)}${condBadge}</div>`;
|
||||
}
|
||||
@@ -786,30 +786,33 @@ function abrirEditorCampo(idx) {
|
||||
const camposOpciones = _campos.filter(f =>
|
||||
['checkbox','radio','select'].includes(f.tipo) && f.id !== c.id && (f.options||[]).length
|
||||
);
|
||||
const condCampoId = c.condicion?.campo_id || '';
|
||||
const condValor = c.condicion?.valor || '';
|
||||
const campoCtrl = camposOpciones.find(f => f.id === condCampoId);
|
||||
const optsCtrl = campoCtrl ? (campoCtrl.options||[]) : [];
|
||||
const condCampoId = c.condicion?.campo_id || '';
|
||||
// soporta tanto valor (string) como valores (array)
|
||||
const condValores = c.condicion?.valores || (c.condicion?.valor ? [c.condicion.valor] : []);
|
||||
const campoCtrl = camposOpciones.find(f => f.id === condCampoId);
|
||||
const optsCtrl = campoCtrl ? (campoCtrl.options||[]) : [];
|
||||
|
||||
const optsCampos = camposOpciones.map(f =>
|
||||
`<option value="${esc(f.id)}" ${f.id===condCampoId?'selected':''}>${esc(f.label)}</option>`
|
||||
).join('');
|
||||
const optsValores = optsCtrl.map(o =>
|
||||
`<option value="${esc(o)}" ${o===condValor?'selected':''}>${esc(o)}</option>`
|
||||
`<option value="${esc(o)}" ${condValores.includes(o)?'selected':''}>${esc(o)}</option>`
|
||||
).join('');
|
||||
|
||||
html += `<hr class="my-2">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold"><i class="fas fa-eye-slash me-1 text-warning"></i>Visibilidad condicional</label>
|
||||
<div class="form-text mb-2">Esta sección (y los campos hasta el siguiente separador) se muestra solo si se cumple la condición.</div>
|
||||
<div class="form-text mb-2">Se muestra si alguno de los valores seleccionados está marcado en el campo elegido. Ctrl+clic para selección múltiple.</div>
|
||||
<select class="form-select form-select-sm mb-2" id="ce-cond-campo" onchange="actualizarOpcionesCondicion(this.value)">
|
||||
<option value="">— Siempre visible —</option>
|
||||
${optsCampos}
|
||||
</select>
|
||||
<select class="form-select form-select-sm" id="ce-cond-valor" ${condCampoId?'':'disabled'}>
|
||||
<option value="">— Selecciona campo primero —</option>
|
||||
<select class="form-select form-select-sm" id="ce-cond-valor" multiple size="5"
|
||||
style="${condCampoId?'':'opacity:.4;pointer-events:none'}"
|
||||
${condCampoId?'':'disabled'}>
|
||||
${optsValores}
|
||||
</select>
|
||||
<div class="form-text">Ctrl+clic para marcar varios valores.</div>
|
||||
</div>`;
|
||||
} else if (c.tipo === 'linked') {
|
||||
html += `<div class="alert alert-info small py-2 mb-0">
|
||||
@@ -913,12 +916,19 @@ function abrirEditorCampo(idx) {
|
||||
function actualizarOpcionesCondicion(campoId) {
|
||||
const selValor = document.getElementById('ce-cond-valor');
|
||||
if (!selValor) return;
|
||||
if (!campoId) { selValor.innerHTML = '<option value="">— Selecciona campo primero —</option>'; selValor.disabled = true; return; }
|
||||
if (!campoId) {
|
||||
selValor.innerHTML = '';
|
||||
selValor.disabled = true;
|
||||
selValor.style.opacity = '.4';
|
||||
selValor.style.pointerEvents = 'none';
|
||||
return;
|
||||
}
|
||||
const campo = _campos.find(f => f.id === campoId);
|
||||
const opts = campo?.options || [];
|
||||
selValor.innerHTML = '<option value="">— Selecciona valor —</option>' +
|
||||
opts.map(o => `<option value="${esc(o)}">${esc(o)}</option>`).join('');
|
||||
selValor.innerHTML = opts.map(o => `<option value="${esc(o)}">${esc(o)}</option>`).join('');
|
||||
selValor.disabled = false;
|
||||
selValor.style.opacity = '';
|
||||
selValor.style.pointerEvents = '';
|
||||
}
|
||||
|
||||
function _insertarKey(key) {
|
||||
@@ -967,8 +977,12 @@ function aplicarCampo() {
|
||||
const condCampo = document.getElementById('ce-cond-campo');
|
||||
const condValor = document.getElementById('ce-cond-valor');
|
||||
if (condCampo !== null) {
|
||||
if (condCampo.value && condValor.value) {
|
||||
c.condicion = { campo_id: condCampo.value, valor: condValor.value };
|
||||
const selVals = condValor
|
||||
? [...condValor.selectedOptions].map(o => o.value).filter(Boolean)
|
||||
: [];
|
||||
if (condCampo.value && selVals.length) {
|
||||
// ponytail: valores[] siempre, forma unificada
|
||||
c.condicion = { campo_id: condCampo.value, valores: selVals };
|
||||
} else {
|
||||
delete c.condicion;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user