diff --git a/demo/js/component/demo.treevaluechoosercombo.js b/demo/js/component/demo.treevaluechoosercombo.js index 8fa557ee1..91e11a72c 100644 --- a/demo/js/component/demo.treevaluechoosercombo.js +++ b/demo/js/component/demo.treevaluechoosercombo.js @@ -5,7 +5,7 @@ Demo.TreeValueChooser = BI.inherit(BI.Widget, { render: function () { var widget = BI.createWidget({ - type: "bi.tree_value_chooser_combo", + type: "bi.tree_value_chooser_insert_combo", width: 300, // items: BI.deepClone(Demo.CONSTANTS.TREEITEMS), itemsCreator: function (op, callback) { diff --git a/dist/2.0/fineui.css b/dist/2.0/fineui.css index 214c1b104..d03531953 100644 --- a/dist/2.0/fineui.css +++ b/dist/2.0/fineui.css @@ -3579,7 +3579,7 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, opacity: 0; } .bi-checkbox.disabled.active .checkbox-content { - border-color: #e8eaed; + border-color: #d0d4da; } .bi-checkbox.disabled.active .checkbox-content:after { opacity: 1; @@ -3587,6 +3587,16 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, .bi-theme-dark .bi-checkbox .checkbox-content { border-color: #9EA6B2; } +.bi-theme-dark .bi-checkbox.active .checkbox-content, +.bi-theme-dark .bi-checkbox:active .checkbox-content { + border-color: #3685f2; +} +.bi-theme-dark .bi-checkbox.disabled .checkbox-content { + background-color: #606479; +} +.bi-theme-dark .bi-checkbox.disabled.active .checkbox-content { + border-color: #606479; +} .bi-file { opacity: 0; filter: alpha(opacity=0); diff --git a/dist/2.0/fineui.ie.js b/dist/2.0/fineui.ie.js index 19230dd03..4ec7479be 100644 --- a/dist/2.0/fineui.ie.js +++ b/dist/2.0/fineui.ie.js @@ -37190,8 +37190,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37199,16 +37199,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37231,6 +37231,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37253,7 +37254,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37291,9 +37292,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -37371,15 +37372,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -37391,6 +37395,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -37418,7 +37423,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -37666,7 +37671,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -37756,6 +37761,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -37801,7 +37807,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -37818,6 +37824,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -37912,6 +37921,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -37919,6 +37929,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -37982,9 +37993,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -38890,7 +38900,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -72488,6 +72499,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -72724,6 +73020,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -82600,10 +83001,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -82863,7 +83273,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -82900,6 +83310,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83029,6 +83448,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83095,6 +83518,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83181,6 +83620,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/2.0/fineui.js b/dist/2.0/fineui.js index 73e1d82c0..f75e83c0c 100644 --- a/dist/2.0/fineui.js +++ b/dist/2.0/fineui.js @@ -37594,8 +37594,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37603,16 +37603,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37635,6 +37635,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37657,7 +37658,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37695,9 +37696,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -37775,15 +37776,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -37795,6 +37799,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -37822,7 +37827,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -38070,7 +38075,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -38160,6 +38165,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -38205,7 +38211,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -38222,6 +38228,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -38316,6 +38325,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -38323,6 +38333,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -38386,9 +38397,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -39294,7 +39304,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -72892,6 +72903,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -73128,6 +73424,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -83004,10 +83405,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -83267,7 +83677,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -83304,6 +83714,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83433,6 +83852,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83499,6 +83922,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83585,6 +84024,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/base.css b/dist/base.css index 5091d3fc2..edecdcf44 100644 --- a/dist/base.css +++ b/dist/base.css @@ -1423,7 +1423,7 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, opacity: 0; } .bi-checkbox.disabled.active .checkbox-content { - border-color: #e8eaed; + border-color: #d0d4da; } .bi-checkbox.disabled.active .checkbox-content:after { opacity: 1; @@ -1431,6 +1431,16 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, .bi-theme-dark .bi-checkbox .checkbox-content { border-color: #9EA6B2; } +.bi-theme-dark .bi-checkbox.active .checkbox-content, +.bi-theme-dark .bi-checkbox:active .checkbox-content { + border-color: #3685f2; +} +.bi-theme-dark .bi-checkbox.disabled .checkbox-content { + background-color: #606479; +} +.bi-theme-dark .bi-checkbox.disabled.active .checkbox-content { + border-color: #606479; +} .bi-file { opacity: 0; filter: alpha(opacity=0); diff --git a/dist/base.js b/dist/base.js index a225544fc..d92486f83 100644 --- a/dist/base.js +++ b/dist/base.js @@ -1727,8 +1727,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -1736,16 +1736,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -1768,6 +1768,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -1790,7 +1791,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -1828,9 +1829,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -1908,15 +1909,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -1928,6 +1932,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -1955,7 +1960,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -2203,7 +2208,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -2293,6 +2298,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -2338,7 +2344,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -2355,6 +2361,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -2449,6 +2458,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -2456,6 +2466,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -2519,9 +2530,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -3427,7 +3437,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -6149,7 +6160,7 @@ BI.PopupView = BI.inherit(BI.Widget, { var tbHeight = this.toolbar ? (this.toolbar.attr("height") || 24) : 0, tabHeight = this.tab ? (this.tab.attr("height") || 24) : 0, toolHeight = ((this.tool && this.tool.attr("height")) || 24) * ((this.tool && this.tool.isVisible()) ? 1 : 0); - var resetHeight = h - tbHeight - tabHeight - toolHeight - 2 * this.options.innerVGap - 2; + var resetHeight = h - tbHeight - tabHeight - toolHeight - 2 * this.options.innerVGap; this.view.resetHeight ? this.view.resetHeight(resetHeight) : this.view.element.css({"max-height": resetHeight + "px"}); }, @@ -10067,8 +10078,10 @@ BI.Input = BI.inherit(BI.Single, { }) .on("input propertychange", function (e) { // 输入内容全选并直接删光,如果按键没放开就失去焦点不会触发keyup,被focusout覆盖了 - // 这个事件在input的属性发生改变的时候就会触发(class的变化也算) - if (BI.isNotNull(keyCode)) { + // 其中propertychange在元素属性发生改变的时候就会触发 是为了兼容IE8 + // 通过keyCode判断会漏掉输入法点击输入(右键粘贴暂缓) + var originalEvent = e.originalEvent; + if (BI.isNull(originalEvent.propertyName) || originalEvent.propertyName === "value") { keyCode = null; inputEventValid = true; self._keydown_ = true; diff --git a/dist/bundle.css b/dist/bundle.css index 214c1b104..d03531953 100644 --- a/dist/bundle.css +++ b/dist/bundle.css @@ -3579,7 +3579,7 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, opacity: 0; } .bi-checkbox.disabled.active .checkbox-content { - border-color: #e8eaed; + border-color: #d0d4da; } .bi-checkbox.disabled.active .checkbox-content:after { opacity: 1; @@ -3587,6 +3587,16 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, .bi-theme-dark .bi-checkbox .checkbox-content { border-color: #9EA6B2; } +.bi-theme-dark .bi-checkbox.active .checkbox-content, +.bi-theme-dark .bi-checkbox:active .checkbox-content { + border-color: #3685f2; +} +.bi-theme-dark .bi-checkbox.disabled .checkbox-content { + background-color: #606479; +} +.bi-theme-dark .bi-checkbox.disabled.active .checkbox-content { + border-color: #606479; +} .bi-file { opacity: 0; filter: alpha(opacity=0); diff --git a/dist/bundle.ie.js b/dist/bundle.ie.js index 19230dd03..4ec7479be 100644 --- a/dist/bundle.ie.js +++ b/dist/bundle.ie.js @@ -37190,8 +37190,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37199,16 +37199,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37231,6 +37231,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37253,7 +37254,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37291,9 +37292,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -37371,15 +37372,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -37391,6 +37395,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -37418,7 +37423,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -37666,7 +37671,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -37756,6 +37761,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -37801,7 +37807,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -37818,6 +37824,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -37912,6 +37921,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -37919,6 +37929,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -37982,9 +37993,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -38890,7 +38900,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -72488,6 +72499,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -72724,6 +73020,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -82600,10 +83001,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -82863,7 +83273,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -82900,6 +83310,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83029,6 +83448,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83095,6 +83518,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83181,6 +83620,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/bundle.js b/dist/bundle.js index 73e1d82c0..f75e83c0c 100644 --- a/dist/bundle.js +++ b/dist/bundle.js @@ -37594,8 +37594,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37603,16 +37603,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37635,6 +37635,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37657,7 +37658,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37695,9 +37696,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -37775,15 +37776,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -37795,6 +37799,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -37822,7 +37827,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -38070,7 +38075,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -38160,6 +38165,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -38205,7 +38211,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -38222,6 +38228,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -38316,6 +38325,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -38323,6 +38333,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -38386,9 +38397,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -39294,7 +39304,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -72892,6 +72903,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -73128,6 +73424,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -83004,10 +83405,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -83267,7 +83677,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -83304,6 +83714,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83433,6 +83852,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83499,6 +83922,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83585,6 +84024,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/core.js b/dist/core.js index 180ca9565..233b3cd61 100644 --- a/dist/core.js +++ b/dist/core.js @@ -19976,7 +19976,7 @@ BI.prepares.push(function () { var i, direct; var leftRight = [], topBottom = []; var isNeedAdaptHeight = false, tbFirst = false, lrFirst = false; - var left, top, pos; + var left, top, pos, firstDir = directions[0]; for (i = 0; i < directions.length; i++) { direct = directions[i]; switch (direct) { @@ -20086,39 +20086,45 @@ BI.prepares.push(function () { } } + // 此处为四个方向放不下时挑空间最大的方向去放置, 也就是说我设置了弹出方向为"bottom,left", + // 最后发现实际弹出方向可能是"top,left",那么此时外界获取popup的方向应该是"top,left" switch (directions[0]) { case "left": case "right": if (BI.DOM.isRightSpaceLarger(combo)) { left = BI.DOM.getRightAdaptPosition(combo, popup, extraWidth).left; + firstDir = "right"; } else { left = BI.DOM.getLeftAdaptPosition(combo, popup, extraWidth).left; + firstDir = "left"; } if (topBottom[0] === "bottom") { pos = BI.DOM.getTopAlignPosition(combo, popup, extraHeight, needAdaptHeight); pos.left = left; - pos.dir = directions[0] + ",bottom"; + pos.dir = firstDir + ",bottom"; return pos; } pos = BI.DOM.getBottomAlignPosition(combo, popup, extraHeight, needAdaptHeight); pos.left = left; - pos.dir = directions[0] + ",top"; + pos.dir = firstDir + ",top"; return pos; default : if (BI.DOM.isBottomSpaceLarger(combo)) { pos = BI.DOM.getBottomAdaptPosition(combo, popup, extraHeight, needAdaptHeight); + firstDir = "bottom"; } else { pos = BI.DOM.getTopAdaptPosition(combo, popup, extraHeight, needAdaptHeight); + firstDir = "top"; } if (leftRight[0] === "right") { left = BI.DOM.getLeftAlignPosition(combo, popup, extraWidth, needAdaptHeight).left; pos.left = left; - pos.dir = directions[0] + ",right"; + pos.dir = firstDir + ",right"; return pos; } left = BI.DOM.getRightAlignPosition(combo, popup, extraWidth).left; pos.left = left; - pos.dir = directions[0] + ",left"; + pos.dir = firstDir + ",left"; return pos; } }, diff --git a/dist/demo.js b/dist/demo.js index 3e07f8eac..910eb5bcc 100644 --- a/dist/demo.js +++ b/dist/demo.js @@ -2700,7 +2700,7 @@ BI.shortcut("demo.center", Demo.Center);Demo.TreeValueChooser = BI.inherit(BI.Wi render: function () { var widget = BI.createWidget({ - type: "bi.tree_value_chooser_combo", + type: "bi.tree_value_chooser_insert_combo", width: 300, // items: BI.deepClone(Demo.CONSTANTS.TREEITEMS), itemsCreator: function (op, callback) { diff --git a/dist/fineui.css b/dist/fineui.css index 8f968e822..1c296a696 100644 --- a/dist/fineui.css +++ b/dist/fineui.css @@ -3572,12 +3572,14 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, border-color: #ffffff; opacity: 1; } +.bi-checkbox.disabled .checkbox-content { + background-color: #d0d4da; +} .bi-checkbox.disabled .checkbox-content:after { opacity: 0; } .bi-checkbox.disabled.active .checkbox-content { - border-color: #e8eaed; - background-color: #d0d4da; + border-color: #d0d4da; } .bi-checkbox.disabled.active .checkbox-content:after { opacity: 1; @@ -3585,6 +3587,16 @@ body .bi-button.button-ignore.disabled.ghost .b-font:before, .bi-theme-dark .bi-checkbox .checkbox-content { border-color: #9EA6B2; } +.bi-theme-dark .bi-checkbox.active .checkbox-content, +.bi-theme-dark .bi-checkbox:active .checkbox-content { + border-color: #3685f2; +} +.bi-theme-dark .bi-checkbox.disabled .checkbox-content { + background-color: #606479; +} +.bi-theme-dark .bi-checkbox.disabled.active .checkbox-content { + border-color: #606479; +} .bi-file { opacity: 0; filter: alpha(opacity=0); diff --git a/dist/fineui.ie.js b/dist/fineui.ie.js index e9fee8755..af5c350f2 100644 --- a/dist/fineui.ie.js +++ b/dist/fineui.ie.js @@ -37435,8 +37435,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37444,16 +37444,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37476,6 +37476,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37498,7 +37499,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37536,9 +37537,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -37616,15 +37617,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -37636,6 +37640,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -37663,7 +37668,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -37911,7 +37916,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -38001,6 +38006,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -38046,7 +38052,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -38063,6 +38069,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -38157,6 +38166,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -38164,6 +38174,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -38227,9 +38238,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -39135,7 +39145,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -72733,6 +72744,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -72969,6 +73265,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -82845,10 +83246,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -83108,7 +83518,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -83145,6 +83555,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83274,6 +83693,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83340,6 +83763,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83426,6 +83865,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/fineui.js b/dist/fineui.js index a3981e4d9..e49d3e252 100644 --- a/dist/fineui.js +++ b/dist/fineui.js @@ -37839,8 +37839,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -37848,16 +37848,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -37880,6 +37880,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -37902,7 +37903,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -37940,9 +37941,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -38020,15 +38021,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -38040,6 +38044,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -38067,7 +38072,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); @@ -38315,7 +38320,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -38405,6 +38410,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -38450,7 +38456,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -38467,6 +38473,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; @@ -38561,6 +38570,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -38568,6 +38578,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -38631,9 +38642,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { @@ -39539,7 +39549,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -73137,6 +73148,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -73373,6 +73669,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -83249,10 +83650,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -83512,7 +83922,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -83549,6 +83959,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -83678,6 +84097,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -83744,6 +84167,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -83830,6 +84269,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/fineui_without_jquery_polyfill.js b/dist/fineui_without_jquery_polyfill.js index c7a9f4297..12a530339 100644 --- a/dist/fineui_without_jquery_polyfill.js +++ b/dist/fineui_without_jquery_polyfill.js @@ -27057,7 +27057,8 @@ BI.Combo = BI.inherit(BI.Widget, { }, showView: function (e) { - if (this.isEnabled() && this.combo.isEnabled()) { + // 减少popup 调整宽高的次数 + if (this.isEnabled() && this.combo.isEnabled() && !this.isViewVisible()) { this._popupView(e); } }, @@ -29779,7 +29780,7 @@ BI.PopupView = BI.inherit(BI.Widget, { var tbHeight = this.toolbar ? (this.toolbar.attr("height") || 24) : 0, tabHeight = this.tab ? (this.tab.attr("height") || 24) : 0, toolHeight = ((this.tool && this.tool.attr("height")) || 24) * ((this.tool && this.tool.isVisible()) ? 1 : 0); - var resetHeight = h - tbHeight - tabHeight - toolHeight - 2 * this.options.innerVGap - 2; + var resetHeight = h - tbHeight - tabHeight - toolHeight - 2 * this.options.innerVGap; this.view.resetHeight ? this.view.resetHeight(resetHeight) : this.view.element.css({"max-height": resetHeight + "px"}); }, @@ -33072,8 +33073,10 @@ BI.Input = BI.inherit(BI.Single, { }) .on("input propertychange", function (e) { // 输入内容全选并直接删光,如果按键没放开就失去焦点不会触发keyup,被focusout覆盖了 - // 这个事件在input的属性发生改变的时候就会触发(class的变化也算) - if (BI.isNotNull(keyCode)) { + // 其中propertychange在元素属性发生改变的时候就会触发 是为了兼容IE8 + // 通过keyCode判断会漏掉输入法点击输入(右键粘贴暂缓) + var originalEvent = e.originalEvent; + if (BI.isNull(originalEvent.propertyName) || originalEvent.propertyName === "value") { keyCode = null; inputEventValid = true; self._keydown_ = true; @@ -55945,6 +55948,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -56181,6 +56469,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -66057,10 +66450,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -66320,7 +66722,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -66357,6 +66759,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -66486,6 +66897,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -66552,6 +66967,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -66638,6 +67069,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/dist/widget.js b/dist/widget.js index 0b623837d..a45f95b02 100644 --- a/dist/widget.js +++ b/dist/widget.js @@ -12952,6 +12952,291 @@ BI.MultiTreeCombo = BI.inherit(BI.Single, { BI.MultiTreeCombo.EVENT_CONFIRM = "MultiTreeCombo.EVENT_CONFIRM"; BI.shortcut("bi.multi_tree_combo", BI.MultiTreeCombo);/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo);/** * 带加载的多选下拉面板 * @class BI.MultiTreePopup * @extends BI.Pane @@ -13188,6 +13473,111 @@ BI.MultiTreeCheckSelectedButton = BI.inherit(BI.Single, { BI.MultiTreeCheckSelectedButton.EVENT_CHANGE = "EVENT_CHANGE"; BI.shortcut("bi.multi_tree_check_selected_button", BI.MultiTreeCheckSelectedButton);/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane);/** * searcher * Created by guy on 15/11/3. * @class BI.MultiTreeSearcher @@ -23064,10 +23454,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -23327,7 +23726,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -23364,6 +23763,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -23493,6 +23901,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -23559,6 +23971,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { @@ -23645,6 +24073,59 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { return this._getChildren(parentValues).length; } });/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo);/** * 简单的复选下拉树控件, 适用于数据量少的情况 * * Created by GUY on 2015/10/29. diff --git a/src/base/tree/ztree/asynctree.js b/src/base/tree/ztree/asynctree.js index 23eb2123a..2273209d9 100644 --- a/src/base/tree/ztree/asynctree.js +++ b/src/base/tree/ztree/asynctree.js @@ -18,7 +18,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { var self = this; var setting = { async: { - enable: false, + enable: false, // 很明显这棵树把异步请求关掉了,所有的异步请求都是手动控制的 otherParam: BI.cjkEncodeDO(paras) }, check: { @@ -108,6 +108,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { return setting; }, + // 用来更新this.options.paras.selectedValues, 和ztree内部无关 _selectTreeNode: function (treeId, treeNode) { var self = this, o = this.options; var parentValues = BI.deepClone(treeNode.parentValues || self._getParentValues(treeNode)); @@ -153,7 +154,7 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { function callback (nodes, hasNext) { self.nodes.addNodes(treeNode, nodes); - + // 展开节点是没有分页的 if (hasNext === true) { BI.delay(function () { times++; @@ -170,6 +171,9 @@ BI.AsyncTree = BI.inherit(BI.TreeView, { } }, + // a,b 两棵树 + // a->b b->a 做两次校验, 构造一个校验后的map + // e.g. 以a为基准,如果b没有此节点,则在map中添加。 如b有,且是全选的, 则在map中构造全选(为什么不添加a的值呢? 因为这次是取并集), 如果b中也有和a一样的存值,就递归 _join: function (valueA, valueB) { var self = this; var map = {}; diff --git a/src/base/tree/ztree/parttree.js b/src/base/tree/ztree/parttree.js index 8718b5efa..c201f2141 100644 --- a/src/base/tree/ztree/parttree.js +++ b/src/base/tree/ztree/parttree.js @@ -45,6 +45,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { BI.AsyncTree.superclass._selectTreeNode.apply(self, arguments); } else { // 如果选中的值中不存在该值不处理 + // 因为反正是不选中,没必要管 var t = this.options.paras.selectedValues; var p = parentValues.concat(name); for (var i = 0, len = p.length; i < len; i++) { @@ -52,6 +53,7 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { if (t == null) { return; } + // 选中中国-江苏, 搜索南京,取消勾选 if (BI.isEmpty(t)) { break; } @@ -115,9 +117,8 @@ BI.PartTree = BI.inherit(BI.AsyncTree, { } var hasNext = !!d.hasNext, nodes = d.items || []; o.paras.lastSearchValue = d.lastSearchValue; - if (nodes.length > 0) { - callback(self._dealWidthNodes(nodes)); - } + // 没有请求到数据也要初始化空树, 如果不初始化, 树就是上一次构造的树, 节点信息都是过期的 + callback(nodes.length > 0 ? self._dealWidthNodes(nodes) : []); self.setTipVisible(nodes.length <= 0); self.loaded(); if (!hasNext) { diff --git a/src/base/tree/ztree/treeview.js b/src/base/tree/ztree/treeview.js index e7de3bbd5..bd87b215e 100644 --- a/src/base/tree/ztree/treeview.js +++ b/src/base/tree/ztree/treeview.js @@ -71,8 +71,8 @@ BI.TreeView = BI.inherit(BI.Pane, { async: { enable: true, url: getUrl, - autoParam: ["id", "name"], - otherParam: BI.cjkEncodeDO(paras) + autoParam: ["id", "name"], // 节点展开异步请求自动提交id和name + otherParam: BI.cjkEncodeDO(paras) // 静态参数 }, check: { enable: true @@ -80,16 +80,16 @@ BI.TreeView = BI.inherit(BI.Pane, { data: { key: { title: "title", - name: "text" + name: "text" // 节点的name属性替换成text }, simpleData: { - enable: true + enable: true // 可以穿id,pid属性的对象数组 } }, view: { showIcon: false, expandSpeed: "", - nameIsHTML: true, + nameIsHTML: true, // 节点可以用html标签代替 dblClickExpand: false }, callback: { @@ -112,6 +112,7 @@ BI.TreeView = BI.inherit(BI.Pane, { if(status.half === true && status.checked === true) { checked = false; } + // 更新此node的check状态, 影响父子关联,并调用beforeCheck和onCheck回调 self.nodes.checkNode(treeNode, !checked, true, true); } @@ -134,7 +135,7 @@ BI.TreeView = BI.inherit(BI.Pane, { } return true; } - BI.Msg.toast("Please Wait。", "warning"); + BI.Msg.toast("Please Wait。", "warning"); // 不展开节点,也不触发onExpand事件 return false; } @@ -172,9 +173,9 @@ BI.TreeView = BI.inherit(BI.Pane, { function ajaxGetNodes (treeNode, reloadType) { var zTree = self.nodes; if (reloadType == "refresh") { - zTree.updateNode(treeNode); + zTree.updateNode(treeNode); // 刷新一下当前节点,如果treeNode.xxx被改了的话 } - zTree.reAsyncChildNodes(treeNode, reloadType, true); + zTree.reAsyncChildNodes(treeNode, reloadType, true); // 强制加载子节点,reloadType === refresh为先清空再加载,否则为追加到现有子节点之后 } function beforeCheck (treeId, treeNode) { @@ -252,15 +253,18 @@ BI.TreeView = BI.inherit(BI.Pane, { } var parent = node.parentValues || self._getParentValues(node); var path = parent.concat(this._getNodeValue(node)); + // 当前节点是全选的,因为上面的判断已经排除了不选和半选 if (BI.isNotEmptyArray(node.children) || checkState.half === false) { this._buildTree(map, path); return; } + // 剩下的就是半选不展开的节点,因为不知道里面是什么情况,所以借助selectedValues(这个是完整的选中情况) var storeValues = BI.deepClone(this.options.paras.selectedValues); var treeNode = this._getTree(storeValues, path); this._addTreeNode(map, parent, this._getNodeValue(node), treeNode); }, + // 获取的是以values最后一个节点为根的子树 _getTree: function (map, values) { var cur = map; BI.any(values, function (i, value) { @@ -272,6 +276,7 @@ BI.TreeView = BI.inherit(BI.Pane, { return cur; }, + // 以values为path一路向里补充map, 并在末尾节点添加key: value节点 _addTreeNode: function (map, values, key, value) { var cur = map; BI.each(values, function (i, value) { @@ -299,7 +304,7 @@ BI.TreeView = BI.inherit(BI.Pane, { var self = this; var hashMap = {}; var rootNoots = this.nodes.getNodes(); - track(rootNoots); + track(rootNoots); // 可以看到这个方法没有递归调用,所以在_getHalfSelectedValues中需要关心全选的节点 function track (nodes) { BI.each(nodes, function (i, node) { var checkState = node.getCheckStatus(); diff --git a/src/component/treevaluechooser/abstract.treevaluechooser.js b/src/component/treevaluechooser/abstract.treevaluechooser.js index b0821bda1..5bd5b7875 100644 --- a/src/component/treevaluechooser/abstract.treevaluechooser.js +++ b/src/component/treevaluechooser/abstract.treevaluechooser.js @@ -92,10 +92,19 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } BI.each(selected, function (k) { var node = self._getTreeNode(parentValues, k); - var newParents = BI.clone(parentValues); - newParents.push(node.value); - createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); - doCheck(newParents, node, selected[k]); + // 找不到就是新增值 + if(BI.isNull(node)) { + createOneJson({ + id: BI.UUID(), + text: k, + value: k + }, BI.UUID(), 0); + } else { + var newParents = BI.clone(parentValues); + newParents.push(node.value); + createOneJson(node, node.parent && node.parent.id, getCount(selected[k], newParents)); + doCheck(newParents, node, selected[k]); + } }); } @@ -355,7 +364,7 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { var result = []; var keyword = op.keyword || ""; var selectedValues = op.selectedValues; - var lastSearchValue = op.lastSearchValue || ""; + var lastSearchValue = op.lastSearchValue || ""; // 一次请求100个,但是搜索是拿全部的,lastSearchValue是上一次遍历到的节点索引 var output = search(); BI.nextTick(function () { callback({ @@ -392,6 +401,15 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { break; } } + + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (op.times === 1) { + var nodes = self._getAddedValueNode([], selectedValues); + result = BI.concat(BI.filter(nodes, function (idx, node) { + var find = BI.Func.getSearchResult([node.text || node.value], keyword); + return find.find.length > 0 || find.match.length > 0; + }), result); + } return output; } @@ -521,6 +539,10 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { halfCheck: state[1] }); } + // 深层嵌套的比较麻烦,这边先实现的是在根节点添加 + if (parentValues.length === 0 && times === 1) { + result = BI.concat(self._getAddedValueNode(parentValues, selectedValues), result); + } BI.nextTick(function () { callback({ items: result, @@ -587,6 +609,22 @@ BI.AbstractTreeValueChooser = BI.inherit(BI.Widget, { } }, + _getAddedValueNode: function (parentValues, selectedValues) { + var nodes = this._getChildren(parentValues); + return BI.map(BI.difference(BI.keys(selectedValues), BI.map(nodes, "value")), function (idx, v) { + return { + id: BI.UUID(), + pId: nodes.length > 0 ? nodes[0].pId : BI.UUID(), + value: v, + text: v, + times: 1, + isParent: false, + checked: true, + halfCheck: false + }; + }); + }, + _getNode: function (selectedValues, parentValues) { var pNode = selectedValues; for (var i = 0, len = parentValues.length; i < len; i++) { diff --git a/src/component/treevaluechooser/combo.treevaluechooser.insert.js b/src/component/treevaluechooser/combo.treevaluechooser.insert.js new file mode 100644 index 000000000..530c13f54 --- /dev/null +++ b/src/component/treevaluechooser/combo.treevaluechooser.insert.js @@ -0,0 +1,54 @@ +/** + * 简单的复选下拉树控件, 适用于数据量少的情况, 可以自增值 + * + * Created by GUY on 2015/10/29. + * @class BI.TreeValueChooserInsertCombo + * @extends BI.Widget + */ +BI.TreeValueChooserInsertCombo = BI.inherit(BI.AbstractTreeValueChooser, { + + _defaultConfig: function () { + return BI.extend(BI.TreeValueChooserInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-tree-value-chooser-insert-combo", + width: 200, + height: 24, + items: null, + itemsCreator: BI.emptyFn + }); + }, + + _init: function () { + BI.TreeValueChooserInsertCombo.superclass._init.apply(this, arguments); + var self = this, o = this.options; + if (BI.isNotNull(o.items)) { + this._initData(o.items); + } + this.combo = BI.createWidget({ + type: "bi.multi_tree_insert_combo", + element: this, + itemsCreator: BI.bind(this._itemsCreator, this), + valueFormatter: BI.bind(this._valueFormatter, this), + width: o.width, + height: o.height + }); + + this.combo.on(BI.MultiTreeCombo.EVENT_CONFIRM, function () { + self.fireEvent(BI.TreeValueChooserInsertCombo.EVENT_CONFIRM); + }); + }, + + setValue: function (v) { + this.combo.setValue(v); + }, + + getValue: function () { + return this.combo.getValue(); + }, + + populate: function (items) { + this._initData(items); + this.combo.populate.apply(this.combo, arguments); + } +}); +BI.TreeValueChooserInsertCombo.EVENT_CONFIRM = "TreeValueChooserInsertCombo.EVENT_CONFIRM"; +BI.shortcut("bi.tree_value_chooser_insert_combo", BI.TreeValueChooserInsertCombo); \ No newline at end of file diff --git a/src/widget/multitree/multi.tree.insert.combo.js b/src/widget/multitree/multi.tree.insert.combo.js new file mode 100644 index 000000000..6133cf92f --- /dev/null +++ b/src/widget/multitree/multi.tree.insert.combo.js @@ -0,0 +1,286 @@ +/** + * 可以往当前选中节点下添加新值的下拉树 + * @class BI.MultiTreeInsertCombo + * @extends BI.Single + */ + +BI.MultiTreeInsertCombo = BI.inherit(BI.Single, { + + constants: { + offset: { + top: 0, + left: 0, + right: 0, + bottom: 25 + } + }, + + _defaultConfig: function () { + return BI.extend(BI.MultiTreeInsertCombo.superclass._defaultConfig.apply(this, arguments), { + baseCls: "bi-multi-tree-insert-combo", + itemsCreator: BI.emptyFn, + valueFormatter: BI.emptyFn, + height: 24 + }); + }, + + _init: function () { + BI.MultiTreeInsertCombo.superclass._init.apply(this, arguments); + + var self = this, o = this.options; + + var isInit = false; + var want2showCounter = false; + + this.storeValue = {value: o.value || {}}; + + this.trigger = BI.createWidget({ + type: "bi.multi_select_trigger", + height: o.height, + valueFormatter: o.valueFormatter, + // adapter: this.popup, + masker: { + offset: this.constants.offset + }, + searcher: { + type: "bi.multi_tree_searcher", + itemsCreator: o.itemsCreator, + popup: { + type: "bi.multi_tree_search_insert_pane", + listeners: [{ + eventName: BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, + action: function () { + self.storeValue.value[self.trigger.getSearcher().getKeyword()] = {}; + self._assertShowValue(); + // setValue以更新paras.value, 之后从search popup中拿到的就能有add的值了 + self.combo.setValue(self.storeValue); + self.trigger.stopEditing(); + } + }] + } + }, + switcher: { + el: { + type: "bi.multi_tree_check_selected_button" + }, + popup: { + type: "bi.multi_tree_check_pane", + itemsCreator: o.itemsCreator + } + }, + value: {value: o.value || {}} + + }); + + this.combo = BI.createWidget({ + type: "bi.combo", + toggle: false, + container: o.container, + el: this.trigger, + adjustLength: 1, + popup: { + type: "bi.multi_tree_popup_view", + ref: function () { + self.popup = this; + self.trigger.setAdapter(this); + }, + listeners: [{ + eventName: BI.MultiTreePopup.EVENT_AFTERINIT, + action: function () { + self.trigger.getCounter().adjustView(); + isInit = true; + if (want2showCounter === true) { + showCounter(); + } + } + }, { + eventName: BI.MultiTreePopup.EVENT_CHANGE, + action: function () { + change = true; + var val = { + type: BI.Selection.Multi, + value: this.hasChecked() ? this.getValue() : {} + }; + self.trigger.getSearcher().setState(val); + self.trigger.getCounter().setButtonChecked(val); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CONFIRM, + action: function () { + self.combo.hideView(); + } + }, { + eventName: BI.MultiTreePopup.EVENT_CLICK_CLEAR, + action: function () { + clear = true; + self.setValue(); + self._defaultState(); + } + }], + itemsCreator: o.itemsCreator, + onLoaded: function () { + BI.nextTick(function () { + self.trigger.getCounter().adjustView(); + self.trigger.getSearcher().adjustView(); + }); + } + }, + value: {value: o.value || {}}, + hideChecker: function (e) { + return triggerBtn.element.find(e.target).length === 0; + } + }); + + var change = false; + var clear = false; // 标识当前是否点击了清空 + + var isSearching = function () { + return self.trigger.getSearcher().isSearching(); + }; + + var isPopupView = function () { + return self.combo.isViewVisible(); + }; + + this.trigger.on(BI.MultiSelectTrigger.EVENT_START, function () { + self.storeValue = {value: self.combo.getValue()}; + this.setValue(self.storeValue); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_STOP, function () { + self.storeValue = {value: this.getValue()}; + self.combo.setValue(self.storeValue); + BI.nextTick(function () { + if (isPopupView()) { + self.combo.populate(); + } + }); + }); + + function showCounter () { + if (isSearching()) { + self.storeValue = {value: self.trigger.getValue()}; + } else if (isPopupView()) { + self.storeValue = {value: self.combo.getValue()}; + } + self.trigger.setValue(self.storeValue); + } + + this.trigger.on(BI.MultiSelectTrigger.EVENT_BEFORE_COUNTER_POPUPVIEW, function () { + if (want2showCounter === false) { + want2showCounter = true; + } + if (isInit === true) { + want2showCounter = null; + showCounter(); + } + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_TRIGGER_CLICK, function () { + self.combo.toggle(); + }); + this.trigger.on(BI.MultiSelectTrigger.EVENT_COUNTER_CLICK, function () { + if (!self.combo.isViewVisible()) { + self.combo.showView(); + } + }); + + this.trigger.on(BI.MultiSelectTrigger.EVENT_CHANGE, function () { + var checked = this.getSearcher().hasChecked(); + var val = { + type: BI.Selection.Multi, + value: checked ? {1: 1} : {} + }; + this.getSearcher().setState(checked ? BI.Selection.Multi : BI.Selection.None); + this.getCounter().setButtonChecked(val); + }); + + this.combo.on(BI.Combo.EVENT_BEFORE_POPUPVIEW, function () { + if (isSearching()) { + return; + } + if (change === true) { + self.storeValue = {value: self.combo.getValue()}; + change = false; + } + self.combo.setValue(self.storeValue); + self.populate(); + + }); + this.combo.on(BI.Combo.EVENT_BEFORE_HIDEVIEW, function () { + if (isSearching()) { + self.trigger.stopEditing(); + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } else { + if (isPopupView()) { + self.trigger.stopEditing(); + self.storeValue = {value: self.combo.getValue()}; + if (clear === true) { + self.storeValue = {value: {}}; + } + self.fireEvent(BI.MultiTreeInsertCombo.EVENT_CONFIRM); + } + } + clear = false; + change = false; + }); + + var triggerBtn = BI.createWidget({ + type: "bi.trigger_icon_button", + width: o.height, + height: o.height, + cls: "multi-select-trigger-icon-button" + }); + triggerBtn.on(BI.TriggerIconButton.EVENT_CHANGE, function () { + self.trigger.getCounter().hideView(); + if (self.combo.isViewVisible()) { + self.combo.hideView(); + } else { + self.combo.showView(); + } + }); + BI.createWidget({ + type: "bi.absolute", + element: this, + items: [{ + el: this.combo, + left: 0, + right: 0, + top: 0, + bottom: 0 + }, { + el: triggerBtn, + right: 0, + top: 0, + bottom: 0 + }] + }); + }, + + _assertShowValue: function () { + this.trigger.getSearcher().setState(this.storeValue); + this.trigger.getCounter().setButtonChecked(this.storeValue); + }, + + _defaultState: function () { + this.trigger.stopEditing(); + this.combo.hideView(); + }, + + setValue: function (v) { + this.storeValue.value = v || {}; + this.combo.setValue({ + value: v || {} + }); + }, + + getValue: function () { + return this.storeValue.value; + }, + + populate: function () { + this.combo.populate.apply(this.combo, arguments); + } +}); + +BI.MultiTreeInsertCombo.EVENT_CONFIRM = "MultiTreeInsertCombo.EVENT_CONFIRM"; + +BI.shortcut("bi.multi_tree_insert_combo", BI.MultiTreeInsertCombo); \ No newline at end of file diff --git a/src/widget/multitree/trigger/multi.tree.search.insert.pane.js b/src/widget/multitree/trigger/multi.tree.search.insert.pane.js new file mode 100644 index 000000000..ccad8fede --- /dev/null +++ b/src/widget/multitree/trigger/multi.tree.search.insert.pane.js @@ -0,0 +1,106 @@ +/** + * + * 在搜索框中输入文本弹出的面板 + * @class BI.MultiTreeSearchInsertPane + * @extends BI.Pane + */ + +BI.MultiTreeSearchInsertPane = BI.inherit(BI.Widget, { + + constants: { + height: 24, + }, + + props: { + baseCls: "bi-multi-tree-search-insert-pane bi-card", + itemsCreator: BI.emptyFn, + keywordGetter: BI.emptyFn + }, + + render: function () { + var self = this, opts = this.options; + + return { + type: "bi.vertical", + items: [{ + type: "bi.text_button", + invisible: true, + ref: function (_ref) { + self.addTip = _ref; + }, + text: BI.i18nText("BI-Basic_Click_To_Add_Text", ""), + height: this.constants.height, + cls: "bi-high-light", + hgap: 5, + handler: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM, opts.keywordGetter()); + } + }, { + type: "bi.part_tree", + tipText: BI.i18nText("BI-No_Select"), + itemsCreator: function (op, callback) { + op.keyword = opts.keywordGetter(); + opts.itemsCreator(op, function (res) { + callback(res); + self.setKeyword(opts.keywordGetter(), res.items); + }); + }, + ref: function (_ref) { + self.partTree = _ref; + }, + value: opts.value, + listeners: [{ + eventName: BI.Controller.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.Controller.EVENT_CHANGE, arguments); + } + }, { + eventName: BI.TreeView.EVENT_CHANGE, + action: function () { + self.fireEvent(BI.MultiTreeSearchInsertPane.EVENT_CHANGE); + } + }] + }] + }; + }, + + setKeyword: function (keyword, nodes) { + var isAddTipVisible = BI.isEmptyArray(nodes); + this.addTip.setVisible(isAddTipVisible); + this.partTree.setVisible(!isAddTipVisible); + isAddTipVisible && this.addTip.setText(BI.i18nText("BI-Basic_Click_To_Add_Text", keyword)); + }, + + hasChecked: function () { + return this.partTree.hasChecked(); + }, + + setValue: function (v) { + this.setSelectedValue(v.value); + }, + + setSelectedValue: function (v) { + v || (v = {}); + this.partTree.setSelectedValue(v); + }, + + getValue: function () { + return this.partTree.getValue(); + }, + + empty: function () { + this.partTree.empty(); + }, + + populate: function (op) { + this.partTree.stroke.apply(this.partTree, arguments); + } +}); + +BI.MultiTreeSearchInsertPane.EVENT_CHANGE = "EVENT_CHANGE"; + +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CONFIRM = "EVENT_CLICK_CONFIRM"; +BI.MultiTreeSearchInsertPane.EVENT_CLICK_CLEAR = "EVENT_CLICK_CLEAR"; +BI.MultiTreeSearchInsertPane.EVENT_ADD_ITEM = "EVENT_ADD_ITEM"; + +BI.shortcut("bi.multi_tree_search_insert_pane", BI.MultiTreeSearchInsertPane); \ No newline at end of file