EXTJS记事本:CompositeField与RowEditor结合

作者:技术拾光时间:2026-06-12 08:32:07浏览:336

在RowEditor中利用CompositeField嵌入三个ComboBox实现物料级联筛选,需手动处理控件初始化与Change事件。通过扩展afterstart事件进行控件赋值,在validateedit中校验重复物料,并在afteredit中手动将值写回记录。关键是通过editor.items.items[0]访问CompositeField子组件。

背景与挑战

在进行BOM(物料清单)编辑时,经常需要在同一个RowEditor中处理级联下拉选择,这在实际开发中并不容易实现。客户的物料种类超过一千种,若仅使用单个ComboBox,查找效率极低。因此,方案设计为通过“物料分类”和“物料品牌”两个ComboBox构建级联筛选。但在RowEditor的一个字段内嵌入多个控件,需要手动管理每个控件的初始化和Change事件。经过三天的调试,最终实现了完整方案。

RowEditor定义与事件处理

首先定义RowEditor实例,并设置保存、取消等文本内容。

var editor=new Ext.ux.grid.RowEditor({
    saveText: '确定',
    cancelText: "放弃",
    commitChangesText: '请确定或放弃修改',
    errorText: '错误'
});

// 取消编辑时,若关键字段值为空则删除空记录
editor.on("canceledit", function(editor, pressed) {
    if (pressed && editor.record.get("materialid") == 0) {
        store.remove(editor.record);
    }
}, this);

/**
 * afterstart事件为自行扩展,在RowEditor显示后立即触发,适合初始化自定义控件。
 * 通过editor.items.items[0]访问CompositeField组件,注意RowEditor的items不是数组而是对象。
 * 最后一个ComboBox的数据依赖前两个级联选取,需在callback中动态加载数据。
 */
editor.on("afterstart", function(editor, rowIndex) {
    var record = store.getAt(rowIndex);
    editor.items.items[0].items.items[0].setValue(record.get("setid"));
    editor.items.items[0].items.items[1].setValue(record.get("category"));
    var t_store = editor.items.items[0].items.items[2].getStore();
    t_store.load({
        params: { category: record.get("category"), setid: record.get("setid") },
        callback: function(r, options, success) {
            if (success)
                editor.items.items[0].items.items[2].setValue(record.get("materialid"));
        }
    });
}, this);

/**
 * validateedit事件在点击确认时触发,添加自定义校验:禁止重复物料。
 * 遍历store中的记录,若物料值重复则提示并删除当前记录。
 */
editor.on("validateedit", function(editor, obj, record, rowIndex) {
    var materialid = editor.items.items[0].items.items[2].getValue();
    var exist = false;
    Ext.each(store.getRange(), function(o, i) {
        if (o != record && o.get("materialid") == materialid) {
            exist = true;
            return (false);
        }
    });
    if (exist) {
        Ext.MessageBox.alert("系统提示", "请勿重复添加");
        store.remove(record);
    }
    return (!exist);
}, this);

/**
 * afteredit在通过校验后执行。由于使用CompositeField,需手动将值写回record。
 * materialid对应物料编号,model对应型号(列显示值)。
 */
editor.on("afteredit", function(editor, obj, record, rowIndex) {
    record.set("materialid", editor.items.items[0].items.items[2].getValue());
    record.set("model", editor.items.items[0].items.items[2].getRawValue());
}, this);

将RowEditor作为插件集成到GridPanel

定义GridPanel,并将editor作为插件传入。

{
    xtype: "grid",
    title: "产品BOM",
    layout: "fit",
    store: store,
    enableDragDrop: false,
    border: false,
    frame: false,
    autoScroll: true,
    plugins: [editor],
    sm: sm,
    height: 340,
    clicksToEdit: 2,
    autoWidth: true,
    viewConfig: { forceFit: true, autoFill: true, markDirty: false }
}

列定义与CompositeField实现

列定义中,物料名称/型号列使用CompositeField,包含三个ComboBox:品牌、类别、物料。通过change事件实现级联加载。

columns: [{
    header: "物料名称/型号",
    dataIndex: "model",
    width: 200,
    menuDisabled: true,
    editor: {
        xtype: "compositefield",
        name: "compositefield",
        items: [
            {
                xtype: "combo",
                mode: "local",
                name: "sets",
                width: 80,
                fieldLabel: "适用产品品牌",
                emptyText: "请选择",
                valueField: "id",
                lazyInit: false,
                value: this.data  this.data.title : "",
                hiddenName: "setid",
                hiddenValue: this.data  this.data.setid : "",
                displayField: "title",
                typeAhead: false,
                forceSelection: true,
                editable: true,
                listeners: {
                    "change": function(combo, newvalue, oldvalue) {
                        var category = editor.items.items[0].items.items[1];
                        var material = editor.items.items[0].items.items[2];
                        var c = category.getValue();
                        var store = material.getStore();
                        store.load({
                            params: { setid: newvalue, category: c },
                            callback: function(r, options, success) {
                                if (success)
                                    material.setValue("");
                            }
                        });
                    }
                },
                triggerAction: "all",
                store: new Ext.data.JsonStore({
                    url: "<%=script_path%>data.asp",
                    root: "data",
                    autoDestroy: true,
                    remoteSort: true,
                    listeners: {
                        "load": function(store, records, option) {
                            var s = Ext.data.Record.create([{ name: "id", type: "int" }, { name: "title", type: "string" }]);
                            store.add(new s({ id: 0, title: "通用" }))
                        }
                    },
                    baseParams: { op: "setList" },
                    totalProperty: "total",
                    autoLoad: true,
                    fields: ["title", "id"]
                })
            },
            {
                xtype: "combo",
                mode: "local",
                width: 60,
                name: "category",
                fieldLabel: "类别",
                emptyText: "请选择",
                valueField: "category",
                lazyInit: false,
                value: this.data  this.data.category : "",
                displayField: "category",
                typeAhead: false,
                forceSelection: true,
                triggerAction: "all",
                listeners: {
                    "change": function(combo, newvalue, oldvalue) {
                        var sets = editor.items.items[0].items.items[0];
                        var material = editor.items.items[0].items.items[2];
                        var setid = sets.getValue();
                        var store = material.getStore();
                        store.load({
                            params: { category: newvalue, setid: setid },
                            callback: function(r, options, success) {
                                if (success)
                                    material.setValue("");
                            }
                        });
                    }
                },
                store: new Ext.data.JsonStore({
                    url: "<%=script_path%>data.asp",
                    root: "data",
                    autoDestroy: true,
                    remoteSort: true,
                    baseParams: { op: "materialCategoryList" },
                    totalProperty: "total",
                    autoLoad: true,
                    fields: ["category"]
                })
            },
            {
                xtype: "combo",
                forceSelection: true,
                editable: true,
                mode: "local",
                name: "material",
                fieldLabel: "物料",
                emptyText: "请选择物料",
                valueField: "id",
                allowBlank: false,
                displayField: "model",
                width: 250,
                lazyInit: false,
                typeAhead: false,
                triggerAction: "all",
                listeners: {
                    "change": function(combo, newvalue, oldvalue) {
                        // 强制CompositeField值变化,使RowEditor能正确触发validateedit和afteredit
                        var comp = editor.items.items[0];
                        comp.setRawValue(combo.getRawValue());
                    }
                },
                store: new Ext.data.JsonStore({
                    url: "<%=script_path%>data.asp",
                    root: "data",
                    autoDestroy: true,
                    remoteSort: true,
                    baseParams: { op: "materialList" },
                    totalProperty: "total",
                    autoLoad: false,
                    fields: ["model", "id"]
                })
            }
        ]
    }
},
{
    header: "数量",
    dataIndex: "qty",
    width: 50,
    menuDisabled: true,
    editor: {
        xtype: 'numberfield',
        minValue: 1,
        allowDecimals: false
    }
},
{
    header: "颜色",
    dataIndex: "color",
    width: 60,
    menuDisabled: true
},
{
    header: "尺寸",
    dataIndex: "size",
    width: 60,
    menuDisabled: true
}]

谨以此记,分享给有需要的朋友。