| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- '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 = `<button type="button" class="ops-multi-btn" aria-expanded="false"></button>
- <div class="ops-multi-menu" hidden>
- <input type="text" class="ops-multi-search" placeholder="搜索${esc(label)}">
- <div class="ops-multi-list"></div>
- <div class="ops-multi-foot"><button type="button" data-all>全选</button><button type="button" data-none>清空</button></div>
- </div>`;
- 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} <em>${emptyLabel || '全部'}</em>`;
- else if (sel.size === 1) {
- const one = items.find((i) => sel.has(i.key));
- text = `${label} <em>${esc(one ? one.label : '1')}</em>`;
- } else text = `${label} <em>${sel.size} 项</em>`;
- btn.innerHTML = text + '<span class="ops-multi-caret">▾</span>';
- }
- 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) => `<label class="ops-multi-item">
- <input type="checkbox" value="${esc(i.key)}"${sel.has(i.key) ? ' checked' : ''}>
- <span>${esc(i.label)}</span>${i.hint ? `<em>${esc(i.hint)}</em>` : ''}
- </label>`).join('')
- : '<div class="ops-multi-empty">没有匹配项</div>';
- 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;
- })();
|