'use strict'; // 通用多选下拉(字段 / 店铺 / 学校共用):按钮 + 搜索 + 复选列表 + 全选/清空 // 用法:window.createMultiSelect(rootEl, { label, items:[{key,label,hint}], selected, allowEmpty, emptyLabel, onChange }) (function () { function esc(value) { const div = document.createElement('div'); div.textContent = String(value == null ? '' : value); return div.innerHTML; } function createMultiSelect(root, { label, items, selected, allowEmpty, emptyLabel, onChange }) { const sel = new Set(selected || []); let keyword = ''; root.innerHTML = ` `; const btn = root.querySelector('.ops-multi-btn'); const menu = root.querySelector('.ops-multi-menu'); const list = root.querySelector('.ops-multi-list'); const search = root.querySelector('.ops-multi-search'); function refreshButton() { let text; if (!sel.size) text = `${label} ${emptyLabel || '全部'}`; else if (sel.size === 1) { const one = items.find((i) => sel.has(i.key)); text = `${label} ${esc(one ? one.label : '1')}`; } else text = `${label} ${sel.size} 项`; btn.innerHTML = text + '▾'; } function renderList() { const kw = keyword.trim().toLowerCase(); const filtered = items.filter((i) => !kw || String(i.label).toLowerCase().includes(kw) || String(i.hint || '').toLowerCase().includes(kw)); list.innerHTML = filtered.length ? filtered.map((i) => ``).join('') : '
没有匹配项
'; list.querySelectorAll('input[type="checkbox"]').forEach((input) => { input.addEventListener('change', () => { if (input.checked) sel.add(input.value); else sel.delete(input.value); if (!allowEmpty && !sel.size) { input.checked = true; sel.add(input.value); return; } refreshButton(); onChange([...sel]); }); }); } btn.addEventListener('click', (e) => { e.stopPropagation(); const willOpen = menu.hidden; document.querySelectorAll('.ops-multi-menu').forEach((m) => { m.hidden = true; }); menu.hidden = !willOpen; btn.setAttribute('aria-expanded', String(willOpen)); if (willOpen) { renderList(); search.focus(); } }); search.addEventListener('input', () => { keyword = search.value; renderList(); }); root.querySelector('[data-all]').addEventListener('click', () => { items.forEach((i) => sel.add(i.key)); refreshButton(); renderList(); onChange([...sel]); }); root.querySelector('[data-none]').addEventListener('click', () => { sel.clear(); if (!allowEmpty && items[0]) sel.add(items[0].key); refreshButton(); renderList(); onChange([...sel]); }); refreshButton(); return { refresh: () => { refreshButton(); renderList(); } }; } let dismissBound = false; function bindMultiSelectDismiss() { if (dismissBound) return; dismissBound = true; document.addEventListener('click', (event) => { document.querySelectorAll('.ops-multi').forEach((root) => { const menu = root.querySelector('.ops-multi-menu'); if (!menu || menu.hidden) return; if (root.contains(event.target)) return; menu.hidden = true; const btn = root.querySelector('.ops-multi-btn'); if (btn) btn.setAttribute('aria-expanded', 'false'); }); }); document.addEventListener('keydown', (event) => { if (event.key !== 'Escape') return; document.querySelectorAll('.ops-multi-menu').forEach((menu) => { menu.hidden = true; }); }); } window.createMultiSelect = createMultiSelect; window.bindMultiSelectDismiss = bindMultiSelectDismiss; })();