import { shortcut, extend, isKey, Selection, remove, pushDistinct, deepClone, createWidget, toPix, isNotNull, last, initial, endWith, bind, nextTick, AbsoluteLayout, contains, map, makeObject, each, values, isNull, Func, filter, isNotEmptyArray, isArray, find, BlankSplitChar } from "@/core"; import { Single, Combo } from "@/base"; import { MultiSelectTrigger, MultiSelectPopupView, MultiSelectCombo, SearchMultiSelectTrigger, SearchMultiSelectPopupView } from "@/widget"; import { MultiSelectBar, TriggerIconButton } from "@/case"; @shortcut() export class SearchMultiTextValueCombo extends Single { static xtype = "bi.search_multi_text_value_combo"; static REQ_GET_DATA_LENGTH = 1; static REQ_GET_ALL_DATA = -1; static EVENT_CONFIRM = "EVENT_CONFIRM"; _defaultConfig() { return extend(super._defaultConfig(...arguments), { baseCls: "bi-multi-select-combo bi-search-multi-text-value-combo", height: 24, items: [], }); } _init() { const o = this.options; const triggerBtn = createWidget({ type: TriggerIconButton.xtype, width: o.height, height: o.height, cls: "multi-select-trigger-icon-button", }); super._init(...arguments); const assertShowValue = () => { isKey(this._startValue) && (this.storeValue.type === Selection.All ? remove(this.storeValue.value, this._startValue) : pushDistinct(this.storeValue.value, this._startValue)); this._updateAllValue(); this._checkError(); this.trigger.getSearcher().setState(this.storeValue); this.trigger.getCounter().setButtonChecked(this.storeValue); }; this.storeValue = deepClone(o.value || {}); this._updateAllValue(); this._assertValue(this.storeValue); this._checkError(); // 标记正在请求数据 this.requesting = false; this.trigger = createWidget({ type: SearchMultiSelectTrigger.xtype, text: o.text, height: toPix(o.height, o.simple ? 1 : 2), // adapter: this.popup, masker: { offset: { left: 0, top: 0, right: 0, bottom: BI.SIZE_CONSANTS.LIST_ITEM_HEIGHT + 1, }, }, allValueGetter: () => this.allValue, valueFormatter: o.valueFormatter, itemsCreator: (op, callback) => { this._itemsCreator(op, (res, ...args) => { if (op.times === 1 && isNotNull(op.keywords)) { // 预防trigger内部把当前的storeValue改掉 this.trigger.setValue(deepClone(this.getValue())); } callback.apply(this, [res, ...args]); }); }, value: this.storeValue, warningTitle: o.warningTitle, }); this.trigger.on(MultiSelectTrigger.EVENT_START, () => { this._setStartValue(""); this.trigger.getSearcher().setValue(this.storeValue); }); this.trigger.on(MultiSelectTrigger.EVENT_STOP, () => { this._setStartValue(""); }); this.trigger.on(MultiSelectTrigger.EVENT_SEARCHING, keywords => { const _last = last(keywords); keywords = initial(keywords || []); if (keywords.length > 0) { this._joinKeywords(keywords, () => { if (endWith(_last, BlankSplitChar)) { this.combo.setValue(this.storeValue); assertShowValue(); this.combo.populate(); this._setStartValue(""); } else { this.combo.setValue(this.storeValue); assertShowValue(); } this._dataChange = true; }); } }); this.trigger.on(MultiSelectTrigger.EVENT_CHANGE, (value, obj) => { if (obj instanceof MultiSelectBar) { this._joinAll(this.getValue(), () => { assertShowValue(); }); } else { this._join(this.getValue(), () => { assertShowValue(); }); } this._dataChange = true; }); this.trigger.on(MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, () => { this.trigger.getCounter().setValue(this.storeValue); }); this.trigger.on(MultiSelectTrigger.EVENT_COUNTER_CLICK, () => { if (!this.combo.isViewVisible()) { this.combo.showView(); } }); this.combo = createWidget({ type: Combo.xtype, cls: o.simple ? "bi-border-bottom" : "bi-border bi-border-radius", toggle: false, container: o.container, el: this.trigger, adjustLength: 1, popup: { type: SearchMultiSelectPopupView.xtype, ref: _ref => { this.popup = _ref; this.trigger.setAdapter(_ref); }, listeners: [ { eventName: MultiSelectPopupView.EVENT_CHANGE, action: () => { this._dataChange = true; this.storeValue = this.popup.getValue(); this._adjust(() => { assertShowValue(); }); }, }, { eventName: MultiSelectPopupView.EVENT_CLICK_CONFIRM, action: () => { this._defaultState(); }, }, { eventName: MultiSelectPopupView.EVENT_CLICK_CLEAR, action: () => { this._dataChange = true; this.setValue(); this._defaultState(); }, } ], itemsCreator: bind(this._itemsCreator, this), valueFormatter: o.valueFormatter, onLoaded: () => { nextTick(() => { this.combo.adjustWidth(); this.combo.adjustHeight(); this.trigger.getCounter().adjustView(); this.trigger.getSearcher().adjustView(); }); }, }, value: o.value, hideChecker: e => triggerBtn.element.find(e.target).length === 0, }); this.combo.on(Combo.EVENT_BEFORE_POPUPVIEW, () => { if (!this.combo.isViewVisible()) { this._dataChange = false; // 标记数据是否发生变化 } this.setValue(this.storeValue); nextTick(() => { this._populate(); }); }); // 当退出的时候如果还在处理请求,则等请求结束后再对外发确定事件 this.wants2Quit = false; this.combo.on(Combo.EVENT_AFTER_HIDEVIEW, () => { // important:关闭弹出时又可能没有退出编辑状态 this.trigger.stopEditing(); if (this.requesting === true) { this.wants2Quit = true; } else { /** * 在存在标红的情况,如果popover没有发生改变就确认需要同步trigger的值,否则对外value值和trigger样式不统一 */ assertShowValue(); this._dataChange && this.fireEvent(SearchMultiTextValueCombo.EVENT_CONFIRM); } }); triggerBtn.on(TriggerIconButton.EVENT_CHANGE, () => { this.trigger.getCounter().hideView(); if (this.combo.isViewVisible()) { this.combo.hideView(); } else { this.combo.showView(); } }); createWidget({ type: AbsoluteLayout.xtype, element: this, items: [ { el: this.combo, left: 0, right: 0, top: 0, bottom: 0, }, { el: triggerBtn, right: 0, top: 0, bottom: 0, } ], }); this._checkError(); } _defaultState() { this.trigger.stopEditing(); this.combo.hideView(); } _assertValue(val) { const o = this.options; val || (val = {}); val.type || (val.type = Selection.Multi); val.value || (val.value = []); remove(val.value, (idx, value) => !contains(map(o.items, "value"), value)); } _makeMap(values) { return makeObject(values || []); } _joinKeywords(keywords, callback) { const digest = items => { const selectedMap = this._makeMap(items); each(keywords, (i, val) => { if (isNotNull(selectedMap[val])) { this.storeValue.type === Selection.Multi ? pushDistinct(this.storeValue.value, val) : remove(this.storeValue.value, val); } }); this._adjust(callback); }; this._assertValue(this.storeValue); this.requesting = true; this._itemsCreator( { type: SearchMultiTextValueCombo.REQ_GET_ALL_DATA, keywords, }, ob => { const values = map(ob.items, "value"); digest(values); } ); } _joinAll(res, callback) { this._assertValue(res); this.requesting = true; this._itemsCreator( { type: SearchMultiTextValueCombo.REQ_GET_ALL_DATA, keywords: [this.trigger.getKey()], }, ob => { const items = map(ob.items, "value"); if (this.storeValue.type === res.type) { let change = false; const _map = this._makeMap(this.storeValue.value); each(items, (i, v) => { if (isNotNull(_map[v])) { change = true; this.storeValue.assist && this.storeValue.assist.push(_map[v]); delete _map[v]; } }); change && (this.storeValue.value = values(_map)); this._adjust(callback); return; } const selectedMap = this._makeMap(this.storeValue.value); const notSelectedMap = this._makeMap(res.value); const newItems = []; each(items, (i, item) => { if (isNotNull(selectedMap[items[i]])) { this.storeValue.assist && this.storeValue.assist.push(selectedMap[items[i]]); delete selectedMap[items[i]]; } if (isNull(notSelectedMap[items[i]])) { remove(this.storeValue.assist, item); newItems.push(item); } }); this.storeValue.value = newItems.concat(values(selectedMap)); this._adjust(callback); } ); } _adjust(callback) { const adjust = () => { if (this.storeValue.type === Selection.All && this.storeValue.value.length >= this._count) { this.storeValue = { type: Selection.Multi, value: [], }; } else if (this.storeValue.type === Selection.Multi && this.storeValue.value.length >= this._count) { this.storeValue = { type: Selection.All, value: [], }; } this._updateAllValue(); this._checkError(); if (this.wants2Quit === true) { this._dataChange && this.fireEvent(SearchMultiTextValueCombo.EVENT_CONFIRM); this.wants2Quit = false; } this.requesting = false; }; if (!this._count) { this._itemsCreator( { type: SearchMultiTextValueCombo.REQ_GET_DATA_LENGTH, }, res => { this._count = res.count; adjust(); callback(); } ); } else { adjust(); callback(); } } _join(res, callback) { this._assertValue(res); this._assertValue(this.storeValue); if (this.storeValue.type === res.type) { const map = this._makeMap(this.storeValue.value); each(res.value, (i, v) => { if (!map[v]) { this.storeValue.value.push(v); remove(this.storeValue.assist, v); map[v] = v; } }); let change = false; each(res.assist, (i, v) => { if (isNotNull(map[v])) { change = true; this.storeValue.assist && this.storeValue.assist.push(map[v]); delete map[v]; } }); change && (this.storeValue.value = values(map)); this._adjust(callback); return; } this._joinAll(res, callback); } _setStartValue(value) { this._startValue = value; this.popup.setStartValue(value); } _getItemsByTimes(items, times) { const res = []; for (let i = (times - 1) * 100; items[i] && i < times * 100; i++) { res.push(items[i]); } return res; } _hasNextByTimes(items, times) { return times * 100 < items.length; } _itemsCreator(options, callback) { const o = this.options; let items = o.items; const keywords = (options.keywords || []).slice(); if (options.keyword) { keywords.push(options.keyword); } each(keywords, (i, kw) => { const search = Func.getSearchResult(items, kw); items = search.match.concat(search.find); }); if (options.selectedValues) { // 过滤 const _filter = makeObject(options.selectedValues, true); items = filter(items, (i, ob) => !_filter[ob.value]); } if (options.type == MultiSelectCombo.REQ_GET_ALL_DATA) { callback({ items, }); return; } if (options.type == MultiSelectCombo.REQ_GET_DATA_LENGTH) { callback({ count: items.length }); return; } callback({ items: this._getItemsByTimes(items, options.times), hasNext: this._hasNextByTimes(items, options.times), }); } _checkError() { let v = this.storeValue.value || []; if (isNotEmptyArray(v)) { v = isArray(v) ? v : [v]; const result = find(this.allValue, (idx, value) => !contains(v, value)); if (isNull(result)) { isNotNull(this.trigger) && this.trigger.setTipType("success"); this.element.removeClass("combo-error"); } else { isNotNull(this.trigger) && this.trigger.setTipType("warning"); this.element.addClass("combo-error"); } } else { if (v.length === this.allValue.length) { isNotNull(this.trigger) && this.trigger.setTipType("success"); this.element.removeClass("combo-error"); } else { isNotNull(this.trigger) && this.trigger.setTipType("warning"); this.element.addClass("combo-error"); } } } _updateAllValue() { this.storeValue = this.storeValue || {}; this.allValue = deepClone(this.storeValue.value || []); } setValue(v) { this.storeValue = deepClone(v || {}); this._updateAllValue(); this._assertValue(this.storeValue); this.combo.setValue(this.storeValue); this._checkError(); } getValue() { return deepClone(this.storeValue); } _populate() { this._count = null; this.combo.populate(); } populate(items) { this.options.items = items; this._populate(); } }