/** * 搜索逻辑控件 * * Created by GUY on 2015/9/28. * @class BI.Searcher * @extends BI.Widget */ import { shortcut, Widget, Controller, extend, createWidget, debounce, bind, endWith, deepWithout, nextTick, isEmptyString, isNull } from "../../core"; import ButtonGroup from "./group.button"; import { Maskers } from "../0.base"; @shortcut() export default class Searcher extends Widget { static xtype = "bi.searcher"; static EVENT_CHANGE = "EVENT_CHANGE"; static EVENT_START = "EVENT_START"; static EVENT_STOP = "EVENT_STOP"; static EVENT_PAUSE = "EVENT_PAUSE"; static EVENT_SEARCHING = "EVENT_SEARCHING"; static EVENT_AFTER_INIT = "EVENT_AFTER_INIT"; _defaultConfig() { return extend(super._defaultConfig(arguments), { baseCls: "bi-searcher", lgap: 0, rgap: 0, tgap: 0, bgap: 0, vgap: 0, hgap: 0, isDefaultInit: false, isAutoSearch: true, // 是否自动搜索 isAutoSync: true, // 是否自动同步数据, 即是否保持搜索面板和adapter面板状态值的统一 chooseType: ButtonGroup.CHOOSE_TYPE_SINGLE, // isAutoSearch为false时启用 onSearch: (op, callback) => { callback([]); }, el: { type: "bi.search_editor", }, popup: { type: "bi.searcher_view", }, adapter: null, masker: { // masker层 offset: {}, }, }); } render() { const { el, lgap, rgap, tgap, bgap, vgap, hgap, isDefaultInit } = this.options; this.editor = createWidget(el, { type: "bi.search_editor", }); createWidget({ type: "bi.vertical", element: this, lgap, rgap, tgap, bgap, vgap, hgap, items: [this.editor], }); isDefaultInit && (this._assertPopupView()); const search = debounce(bind(this._search, this), BI.EVENT_RESPONSE_TIME, { "leading": true, "trailing": false, }); this.editor.on(Controller.EVENT_CHANGE, (type) => { switch (type) { case BI.Events.STARTEDIT: this._startSearch(); break; case BI.Events.EMPTY: this._stopSearch(); break; case BI.Events.CHANGE: search(); break; case BI.Events.PAUSE: if (endWith(this.getValue(), BI.BlankSplitChar)) { this._pauseSearch(); } break; default: break; } }); } _assertPopupView() { const { masker, popup, chooseType, isAutoSync, adapter } = this.options; if ((masker && !Maskers.has(this.getName())) || (masker === false && !this.popupView)) { this.popupView = createWidget(popup, { type: "bi.searcher_view", chooseType: chooseType, }); this.popupView.on(Controller.EVENT_CHANGE, (type, value, obj, ...args) => { this.fireEvent(Controller.EVENT_CHANGE, type, value, obj, ...args); if (type === BI.Events.CLICK) { if (isAutoSync) { const values = adapter && adapter.getValue(); switch (chooseType) { case ButtonGroup.CHOOSE_TYPE_SINGLE: adapter && adapter.setValue([obj.getValue()]); break; case ButtonGroup.CHOOSE_TYPE_MULTI: if (!obj.isSelected()) { adapter && adapter.setValue(deepWithout(values, obj.getValue())); } values.push(obj.getValue()); adapter && adapter.setValue(values); break; default: break; } } this.fireEvent(Searcher.EVENT_CHANGE, value, obj); } }); nextTick(() => { this.fireEvent(Searcher.EVENT_AFTER_INIT); }); } if (masker && !Maskers.has(this.getName())) { Maskers.create(this.getName(), adapter, extend({ container: this, render: this.popupView, }, masker), this); } } _startSearch() { this._assertPopupView(); this._stop = false; this._isSearching = true; this.fireEvent(Searcher.EVENT_START); this.popupView.startSearch && this.popupView.startSearch(); // 搜索前先清空dom // BI.Maskers.get(this.getName()).empty(); nextTick((name) => { Maskers.show(name); }, this.getName()); } _pauseSearch() { this._stop = true; nextTick((name) => { Maskers.hide(name); }, this.getName()); if (this._isSearching === true) { this.popupView && this.popupView.pauseSearch && this.popupView.pauseSearch(); this.fireEvent(Searcher.EVENT_PAUSE); } this._isSearching = false; } _stopSearch() { const name = this.getName(); this._stop = true; Maskers.hide(name); if (this._isSearching === true) { this.popupView && this.popupView.stopSearch && this.popupView.stopSearch(); this.fireEvent(Searcher.EVENT_STOP); } this._isSearching = false; } _search() { const { isAutoSearch, adapter, isAutoSync, onSearch } = this.options; const keyword = this.editor.getValue(); if (keyword === "" || this._stop) { return; } if (isAutoSearch) { const items = (adapter && ((adapter.getItems && adapter.getItems()) || adapter.attr("items"))) || []; const finding = BI.Func.getSearchResult(items, keyword); const match = finding.match, find = finding.find; this.popupView.populate(find, match, keyword); isAutoSync && adapter && adapter.getValue && this.popupView.setValue(adapter.getValue()); this.fireEvent(Searcher.EVENT_SEARCHING); return; } this.popupView.loading && this.popupView.loading(); onSearch({ times: 1, keyword: keyword, selectedValues: adapter && adapter.getValue(), }, (searchResult, matchResult, ...arg) => { if (!this._stop && keyword === this.editor.getValue()) { const args = [searchResult, matchResult, ...arg]; if (args.length > 0) { args.push(keyword); } Maskers.show(this.getName()); this.popupView.populate.apply(this.popupView, args); isAutoSync && adapter && adapter.getValue && this.popupView.setValue(adapter.getValue()); this.popupView.loaded && this.popupView.loaded(); this.fireEvent(Searcher.EVENT_SEARCHING); } }); } _getLastSearchKeyword() { if (this.isValid()) { const res = this.editor.getValue().split(/\u200b\s\u200b/); if (isEmptyString(res[res.length - 1])) { res = res.slice(0, res.length - 1); } return isNull(res) ? "" : res[res.length - 1]; } } setAdapter(adapter) { this.options.adapter = adapter; Maskers.remove(this.getName()); } doSearch() { if (this.isSearching()) { this._search(); } } stopSearch() { this._stopSearch();// 先停止搜索,然后再去设置editor为空 // important:停止搜索必须退出编辑状态,这里必须加上try(input框不显示时blur会抛异常) try { this.editor.blur(); } catch (e) { if (!this.editor.blur) { throw new Error("editor没有实现blur方法"); } } finally { this.editor.setValue(""); } } isSearching() { return this._isSearching; } isViewVisible() { return this.editor.isEnabled() && Maskers.isVisible(this.getName()); } getView() { return this.popupView; } hasMatched() { this._assertPopupView(); return this.popupView.hasMatched(); } adjustHeight() { if (Maskers.has(this.getName()) && Maskers.get(this.getName()).isVisible()) { Maskers.show(this.getName()); } } adjustView() { this.isViewVisible() && Maskers.show(this.getName()); } setValue(v) { if (isNull(this.popupView)) { this.options.popup.value = v; } else { this.popupView.setValue(v); } } getKeyword() { return this._getLastSearchKeyword(); } getKeywords() { return this.editor.getKeywords(); } getValue() { const { isAutoSync, adapter, popup } = this.options; if (isAutoSync && adapter && adapter.getValue) { return adapter.getValue(); } if (this.isSearching()) { return this.popupView.getValue(); } else if (adapter && adapter.getValue) { return adapter.getValue(); } if (isNull(this.popupView)) { return popup.value; } return this.popupView.getValue(); } populate(result, searchResult, keyword) { const { isAutoSync, adapter } = this.options; this._assertPopupView(); this.popupView.populate.apply(this.popupView, arguments); if (isAutoSync && adapter && adapter.getValue) { this.popupView.setValue(adapter.getValue()); } } empty() { this.popupView && this.popupView.empty(); } destroyed() { Maskers.remove(this.getName()); } focus() { this.editor.focus(); } blur() { this.editor.blur(); } setWaterMark(v) { this.editor.setWaterMark(v); } }