feat: tree-style Dropdown.

This commit is contained in:
2026-08-17 15:31:24 +08:00
parent 2e1bb9ad20
commit af00428d14
+41 -40
View File
@@ -104,6 +104,7 @@ function getValue(it, valuekey, textkey) {
* @property {string} [textKey=text] - 文本关键字 * @property {string} [textKey=text] - 文本关键字
* @property {string} [valueKey=value] - 值关键字 * @property {string} [valueKey=value] - 值关键字
* @property {string} [htmlKey=html] - 源码显示的关键字 * @property {string} [htmlKey=html] - 源码显示的关键字
* @property {string} [childKey=children] - 子集合的关键字
* @property {Function} [htmlTemplate] - 模板创建函数 * @property {Function} [htmlTemplate] - 模板创建函数
* @property {number} [maxLength=500] - 最大输入长度 * @property {number} [maxLength=500] - 最大输入长度
* @property {boolean} [multiSelect] - 是否允许多选 * @property {boolean} [multiSelect] - 是否允许多选
@@ -144,6 +145,7 @@ export class Dropdown {
options.textKey ??= 'text'; options.textKey ??= 'text';
options.valueKey ??= 'value'; options.valueKey ??= 'value';
options.htmlKey ??= 'html'; options.htmlKey ??= 'html';
options.childKey ??= 'children';
options.maxLength ??= 500; options.maxLength ??= 500;
this._var.options = options; this._var.options = options;
const getText = options.getText; const getText = options.getText;
@@ -303,11 +305,16 @@ export class Dropdown {
return; return;
} }
const valuekey = this._var.options.valueKey; const valuekey = this._var.options.valueKey;
const childkey = this._var.options.childKey;
function reduceItems(list, id, level = 0) { function reduceItems(list, id, level = 0) {
if (!Array.isArray(list)) { if (!Array.isArray(list)) {
return []; return [];
} }
return list.reduce((array, item) => [...array, { __p: id, __level: level, ...item }, ...reduceItems(item.children, item[valuekey], level + 1)], []); return list.reduce((array, item) => [
...array,
(id == null ? item : { __p: String(id), __level: level, ...item }),
...reduceItems(item[childkey], item[valuekey], level + 1)
], []);
} }
this._var.source = reduceItems(list); this._var.source = reduceItems(list);
if (this._expanded) { if (this._expanded) {
@@ -580,19 +587,18 @@ export class Dropdown {
const multiselect = this.multiSelect; const multiselect = this.multiSelect;
const valuekey = this._var.options.valueKey; const valuekey = this._var.options.valueKey;
const textkey = this._var.options.textKey; const textkey = this._var.options.textKey;
const childkey = this._var.options.childKey;
const allchecked = this._var.allChecked; const allchecked = this._var.allChecked;
const selectedlist = this.selectedList; const selectedlist = this.selectedList;
source.forEach((item, i) => { source.forEach((item, i) => {
let val = getValue(item, valuekey, textkey); const val = String(getValue(item, valuekey, textkey));
if (typeof val !== 'string') {
val = String(val);
}
if (multiselect) { if (multiselect) {
const selected = selectedlist.some(s => String(getValue(s, valuekey, textkey)) === val); const selected = selectedlist.some(s => String(getValue(s, valuekey, textkey)) === val);
if (allchecked || selected) { if (allchecked || selected) {
item.__checked = 1; item.__checked = 1;
source.filter(it => it.__p === val).forEach(it => it.__checked = 1);
} else { } else {
const indeterminate = selectedlist.some(s => this._contains(String(getValue(s, valuekey, textkey)), item, valuekey, textkey)); const indeterminate = selectedlist.some(s => this._contains(String(getValue(s, valuekey, textkey)), item, valuekey, textkey, childkey));
if (indeterminate) { if (indeterminate) {
item.__checked = 2; item.__checked = 2;
} }
@@ -609,10 +615,10 @@ export class Dropdown {
} }
} }
_contains(it, item, valuekey, textkey) { _contains(it, item, valuekey, textkey, childkey) {
if (Array.isArray(item.children)) { if (Array.isArray(item[childkey])) {
for (let t of item.children) { for (let t of item[childkey]) {
if (it === getValue(t, valuekey, textkey)) { if (it === String(getValue(t, valuekey, textkey))) {
return true; return true;
} }
if (this._contains(it, t, valuekey, textkey)) { if (this._contains(it, t, valuekey, textkey)) {
@@ -629,34 +635,32 @@ export class Dropdown {
const textkey = this._var.options.textKey; const textkey = this._var.options.textKey;
const template = this._var.options.htmlTemplate; const template = this._var.options.htmlTemplate;
const htmlkey = this._var.options.htmlKey; const htmlkey = this._var.options.htmlKey;
const childkey = this._var.options.childKey;
const selected = this.selected; const selected = this.selected;
let scrolled; let scrolled;
array.forEach((item, i) => { array.forEach((item, i) => {
let val = getValue(item, valuekey, textkey); const val = String(getValue(item, valuekey, textkey));
if (typeof val !== 'string') {
val = String(val);
}
const li = createElement('li'); const li = createElement('li');
li.dataset.value = val; li.dataset.value = val;
li.title = item[textkey]; li.title = item[textkey];
if (item.__level > 0) { if (!isNaN(item.__level) && item.__level > 0) {
li.style.marginLeft = `${item.__level * 24}px`; li.style.marginLeft = `${item.__level * 24}px`;
} }
const wrapper = createElement('span', 'li-wrapper', const wrapper = createElement('span', 'li-wrapper',
createElement('span', span => { createElement('span', span => {
// events // events
span.className = 'ui-expandor'; span.className = 'ui-expandor';
if (Array.isArray(item.children) && item.children.length > 0) { // if (Array.isArray(item[childkey]) && item[childkey].length > 0) {
span.classList.add('active'); // span.classList.add('active');
function hideChildren(children) { // function hideChildren(children) {
for (let c of children) { // for (let c of children) {
c.__visible = false; // c.__visible = false;
if (Array.isArray(c.children)) { // if (Array.isArray(c[childkey])) {
hideChildren(c.children); // hideChildren(c[childkey]);
} // }
} // }
} // }
} // }
}, },
createIcon('fa-solid', 'caret-down') createIcon('fa-solid', 'caret-down')
) )
@@ -713,6 +717,7 @@ export class Dropdown {
const textkey = this._var.options.textKey; const textkey = this._var.options.textKey;
const template = this._var.options.htmlTemplate; const template = this._var.options.htmlTemplate;
const htmlkey = this._var.options.htmlKey; const htmlkey = this._var.options.htmlKey;
const childkey = this._var.options.childKey;
const source = this.source; const source = this.source;
if (checkbox.getAttribute('isall') === '1') { if (checkbox.getAttribute('isall') === '1') {
const allchecked = this._var.allChecked = checkbox.checked; const allchecked = this._var.allChecked = checkbox.checked;
@@ -725,19 +730,15 @@ export class Dropdown {
const val = checkbox.dataset.value; const val = checkbox.dataset.value;
const getSelectedList = () => { const getSelectedList = () => {
const list = []; const list = [];
const cache = {};
source.filter(it => it.__p == null).forEach(it => { source.filter(it => it.__p == null).forEach(it => {
const key = String(getValue(it, valuekey, textkey));
if (it.__checked === 1) { if (it.__checked === 1) {
list.push(it); list.push(it);
cache[getValue(it, valuekey, textkey)] = true; } else if (it.__checked === 2) {
const children = source.filter(c => c.__checked === 1 && c.__p === key);
if (children.length > 0) {
Array.prototype.push.apply(list, children);
} }
});
source.filter(it => it.__p != null).forEach(it => {
if (cache[it.__p]) {
return;
}
if (it.__checked === 1) {
list.push(it);
} }
}); });
return list; return list;
@@ -746,15 +747,16 @@ export class Dropdown {
item.__checked = 1; item.__checked = 1;
if (item.__p != null) { if (item.__p != null) {
// parent // parent
if (!source.filter(it => it.__p === item.__p).some(it => it.__checked !== 1)) {
const parent = source.filter(it => String(getValue(it, valuekey, textkey)) === item.__p)[0]; const parent = source.filter(it => String(getValue(it, valuekey, textkey)) === item.__p)[0];
if (parent != null) { if (parent != null) {
if (!source.filter(it => it.__p === item.__p).some(it => it.__checked !== 1)) {
parent.__checked = 1; parent.__checked = 1;
} else if (parent.__checked !== 2) {
parent.__checked = 2;
} }
} }
} }
if (item.children?.length > 0) { if (item[childkey]?.length > 0) {
// children
source.filter(it => it.__p === val).forEach(it => it.__checked = 1); source.filter(it => it.__p === val).forEach(it => it.__checked = 1);
} }
if (!source.some(it => it.__checked !== 1)) { if (!source.some(it => it.__checked !== 1)) {
@@ -767,10 +769,9 @@ export class Dropdown {
list = getSelectedList(); list = getSelectedList();
} }
} else { } else {
if (item.children?.length > 0) { if (item[childkey]?.length > 0) {
if (item.__checked === 2) { if (item.__checked === 2) {
item.__checked = 0; item.__checked = 0;
// children
source.filter(it => it.__p === val).forEach(it => it.__checked = 0); source.filter(it => it.__p === val).forEach(it => it.__checked = 0);
} else { } else {
item.__checked = 2; item.__checked = 2;