自己研究许久未果,后来在领导的帮助下在google搜索下找到示例。现在extjs已经不再流行,网上面的例子都已经不再适用。
贴网址:https://fiddle.sencha.com/fiddle/19rr?height=600
效果如图:
原文的代码有几个bug,第一是不能使用grouping的startCollapsed属性。因为原来的TPL渲染的视图会在再次渲染的过程中破坏。下文代码我会标注出来,目前发现的bug均已修复。
原文的选中逻辑只能使用{xtype: 'checkcolumn', dataIndex:'isChecked'}的方式,后续我添加了兼容设置grid的selModel: {type: 'checkboxmodel'}的方式,但是此方式我没有直接使用(由于原作者的代码里面大部分用了checkDataIndex: 'isChecked'来做逻辑处理),因此使用者请注意可能出现bug。
/*************************************************
* author : unknown Netizen
* function: CheckboxGroupingFeature
* history: created by MrJGuo 2019/5/31 17:58
* warn:引用的grid请使用checkboxmodel模式(selModel: {type: 'checkboxmodel'})
* 或者定义第一行{xtype: 'checkcolumn', dataIndex:'isChecked'}
* forExample :
* Ext.create('Ext.grid.Panel', {
title: 'Employees',
store: store,
selModel: {
type: 'checkboxmodel',
},
columns: [ {
text: 'Name',
dataIndex: 'name'
}, {
text: 'Seniority',
dataIndex: 'seniority'
}],
features: [{
ftype: 'checkboxGrouping',
enableGroupingMenu: false,
hideGroupHeader: true
}],
width: 500,
height: 600,
renderTo: Ext.getBody()
});
*************************************************/
Ext.define('SDE.ux.field.CheckboxGroupingFeature', {
extend: 'Ext.grid.feature.Grouping',
alias: 'feature.checkboxGrouping',
/** @property */
targetCls: 'group-checkbox',
/** @property */
checkDataIndex: 'isChecked',
startCollapsed: true,
constructor: function(config) {
//此处原文没有判断values.record为空的情况 因此startCollapsed属性设置视图会出现问题
config.groupHeaderTpl = ['<input class="' + this.targetCls + '" {[values.record ? values.record.get("' + this.checkDataIndex + '") ? "checked" : "" :""]} type="checkbox"> {name}'];
this.callParent(arguments);
},
init: function(grid) {
var store = grid.getStore();
if (store) {
store.on('update', this.onStoreUpdate, this);
}
this.callParent(arguments);
},
setupRowData: function(record, idx, rowValues) {
this.callParent(arguments);
// Ext JS 6 vs Ext JS 5.1.1 vs Ext JS 5.1.0-
var groupInfo = this.groupRenderInfo || this.metaGroupCache || this.groupInfo;
groupInfo.record = this.getParentRecord(record.get(this.getGroupField()));
},
/**
* This method will only run once... on the initial load of the view... this
* is so we can check the store for the grouped item's children... if they're
* all checked, then we need to set the private variable to checked
*/
checkAllGroups: function(groupName) {
var store = this.view.getStore();
var groupField = this.getGroupField();
if (store) {
var groups = store.getGroups();
if (groups) {
groups.each(function(groupRec) {
var allChecked = true;
var groupKey = groupRec.getGroupKey();
var checkGroup = true;
if (groupName) {
if (groupKey !== groupName) {
checkGroup = false;
}
}
if (checkGroup) {
groupRec.each(function(rec) {
allChecked = rec.get(this.checkDataIndex);
groupName = rec.get(groupField);
if (allChecked === false) {
return false;
}
}, this);
this.updateParentRecord(groupName, allChecked);
}
}, this);
}
}
},
updateParentRecord: function(groupName, checked,store) {
var parentRecord = this.getParentRecord(groupName,store);
if (parentRecord) {
parentRecord.set(this.checkDataIndex, checked);
this.refreshView();
}
},
getParentRecord: function(groupName,store) {
var parentRecord;
var metaGroup;
// For Ext JS 6 and 5.1.1
if (this.getMetaGroup) {
metaGroup = this.getMetaGroup(groupName);
}
// For Ext JS 5.1-
else {
metaGroup = this.groupCache[groupName];
}
if (metaGroup) {
if (metaGroup.placeholder) {
//此处如果设置了startCollapsed为true,第一次渲染的时候是没有占位符的
parentRecord = metaGroup.placeholder;
} else {
//手动添加占位符、第一次渲染的时候没有点击展开或者收缩 源码是没有做添加占位符的操作的
parentRecord = this.addPlaceHolderByGroup(groupName,metaGroup,store);
}
}
return parentRecord;
},
/**
* This might break... we're using a private variable here... but this
* is the only way we can refresh the view without breaking any sort of
* scrolling... I'm not sure how to only refresh the group header itself, so
* I'm keeping the groupName as a param passing in... might be able to figure
* this out later
*
* ###### Already perfected by MrJGuo 2019.6.24
* @param {String} groupName
*/
refreshView: function(groupName) {
var view = this.view;
if (view) {
view.refreshView();
}
},
onStoreUpdate: function(store, record, operation, modifiedFieldNames, details, eOpts) {
var grid = this.grid;
if (!this.updatingRecords && grid && record) {
var groupName = record.get(this.getGroupField());
this.checkAllGroups(groupName);
grid.setSelection(record);
this.refreshView(groupName);
}
},
onGroupClick: function(grid, node, group, event, eOpts) {
if (event && grid) {
var target = event.getTarget('.' + this.targetCls);
var store = grid.getStore();
var groupRecord = this.getRecordGroup(event.record);
var groupField = store .getGroupField();
if (target && store && groupRecord && groupField) {
var checked = target.checked;
this.updatingRecords = true;
if (grid.getColumnManager().columns[0].dataIndex === this.checkDataIndex) {
groupRecord.each(function(rec, index) {
rec.beginEdit();
rec.set(this.checkDataIndex, checked);
rec.endEdit(true);
}, this);
} else if (grid.selModel.type === 'checkboxmodel') {
grid.getStore().each(function(rec, index){
if(rec.get(groupField) === group){
if (!checked) {
grid.getSelectionModel().deselect(index, true);
} else {
grid.getSelectionModel().select(index, true);
}
}
},this);
}
this.updatingRecords = false;
this.updateParentRecord(group, checked,store);
} else {
this.callParent(arguments);
}
}
},
addPlaceHolderByGroup : function (groupName,metaGroup,store) {
if (!store) {
return;
}
if (!metaGroup.placeholder) {
var Model = store.getModel(),
modelData = {},
key = groupName,
groupPlaceholder;
modelData[store.getGroupField()] = key;
groupPlaceholder = metaGroup.placeholder = new Model(modelData);
groupPlaceholder.isNonData = groupPlaceholder.isCollapsedPlaceholder = true;
// Adding the groupKey instead of storing a reference to the group
// itself. The latter can cause problems if the store is reloaded and the referenced
// group is lost. See EXTJS-18655
groupPlaceholder.groupKey = key;
}
return metaGroup.placeholder;
}
});
使用方式已经在注释头描述清楚,后续有使用出现的bug再修改。刚入手extjs,如有不当之处还请指出。