'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;
})();