js SelectRenderer封装类
/**
 * Select渲染器
 */
class SelectRenderer {
    constructor(config = {}) {
        this.config = $.extend({
            placeholder: true,
            placeholderText: '请选择',
            allowClear: false,
            searchable: false,
            ajax: false,
            data: [],
            valueField: 'value',
            textField: 'text',
            disabledField: 'disabled',
            selectedField: 'selected',
            groupField: 'group'
        }, config);
    }

    /**
     * 渲染单个select
     * @param {jQuery|string} selector - 选择器或jQuery对象
     * @param {Array|Object} data - 数据
     * @param {Object} options - 选项
     */
    render(selector, data = null, options = {}) {
        var $select = $(selector);
        if (!$select.length) return;

        var config = $.extend({}, this.config, options);
        var renderData = data || config.data;

        // 清空现有选项
        $select.empty();

        // 添加占位符
        if (config.placeholder) {
            var $placeholder = $('<option>', {
                value: '',
                text: config.placeholderText
            });

            if (config.allowClear) {
                $placeholder.attr('data-allow-clear', 'true');
            }

            $select.append($placeholder);
        }

        // 渲染选项
        if ($.isArray(renderData)) {
            this._renderOptions($select, renderData, config);
        } else if (renderData && renderData.url) {
            this._renderWithAjax($select, renderData, config);
        }

        // 触发渲染完成事件
        $select.trigger('select:rendered');

        return $select;
    }

    /**
     * 渲染多个select
     * @param {string} className - 类选择器
     * @param {Array} data - 数据
     * @param {Object} options - 选项
     */
    renderAll(className, data = null, options = {}) {
        var self = this;
        var results = [];

        $(className).each(function() {
            var $select = $(this);
            var specificOptions = $.extend({}, options);

            // 从data-*属性读取配置
            var dataAttrs = self._getDataAttributes($select);
            $.extend(specificOptions, dataAttrs);

            results.push(self.render($select, data, specificOptions));
        });

        return results;
    }

    /**
     * 内部方法：渲染选项
     */
    _renderOptions($select, data, config) {
        var groups = {};

        $.each(data, function(index, item) {
            // 分组处理
            if (config.groupField && item[config.groupField]) {
                var groupName = item[config.groupField];
                if (!groups[groupName]) {
                    groups[groupName] = [];
                }
                groups[groupName].push(item);
            } else {
                var $option = $('<option>', {
                    value: item[config.valueField] || item.value || '',
                    text: item[config.textField] || item.text || '',
                    disabled: item[config.disabledField] || item.disabled || false,
                    selected: item[config.selectedField] || item.selected || false
                });

                // 添加自定义属性
                $.each(item, function(key, value) {
                    if (!['value', 'text', 'disabled', 'selected'].includes(key)) {
                        $option.attr('data-' + key, value);
                    }
                });

                $select.append($option);
            }
        });

        // 渲染分组
        this._renderOptionGroups($select, groups, config);
    }

    /**
     * 内部方法：渲染分组
     */
    _renderOptionGroups($select, groups, config) {
        $.each(groups, function(groupName, items) {
            var $optgroup = $('<optgroup>', {
                label: groupName
            });

            $.each(items, function(index, item) {
                var $option = $('<option>', {
                    value: item[config.valueField] || item.value || '',
                    text: item[config.textField] || item.text || '',
                    disabled: item[config.disabledField] || item.disabled || false,
                    selected: item[config.selectedField] || item.selected || false
                });

                $optgroup.append($option);
            });

            $select.append($optgroup);
        });
    }

    /**
     * 内部方法：通过AJAX渲染
     */
    _renderWithAjax($select, ajaxConfig, config) {
        var self = this;

        // 显示加载状态
        $select.prop('disabled', true).addClass('loading');

        $.ajax($.extend({
            type: 'GET',
            dataType: 'json',
            success: function(response) {
                // 处理响应数据
                var data = response;
                if (ajaxConfig.dataProcessor && typeof ajaxConfig.dataProcessor === 'function') {
                    data = ajaxConfig.dataProcessor(response);
                } else if (response.data) {
                    data = response.data;
                }

                // 渲染数据
                self._renderOptions($select, data, config);

                // 恢复select状态
                $select.prop('disabled', false).removeClass('loading');

                // 触发完成事件
                $select.trigger('select:ajax:complete', [response]);
            },
            error: function(xhr, status, error) {
                console.error('加载下拉框数据失败:', error);

                // 添加错误选项
                $select.empty();
                $select.append($('<option>', {
                    value: '',
                    text: '数据加载失败',
                    disabled: true
                }));

                // 恢复状态
                $select.prop('disabled', false).removeClass('loading');

                // 触发错误事件
                $select.trigger('select:ajax:error', [xhr, status, error]);
            }
        }, ajaxConfig));
    }

    /**
     * 获取data-*属性
     */
    _getDataAttributes($element) {
        var attrs = {};
        var data = $element.data();

        // 转换属性名
        $.each(data, function(key, value) {
            if (key.startsWith('select')) {
                var newKey = key.replace(/^select/, '');
                newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
                attrs[newKey] = value;
            }
        });

        return attrs;
    }

    /**
     * 设置选中值
     */
    setValue($select, value) {
        $select.val(value).trigger('change');
        return this;
    }

    /**
     * 启用/禁用
     */
    setDisabled($select, disabled) {
        $select.prop('disabled', disabled);
        return this;
    }

    /**
     * 重新加载数据
     */
    reload($select) {
        var dataAttrs = this._getDataAttributes($select);
        if (dataAttrs.url) {
            this._renderWithAjax($select, { url: dataAttrs.url }, dataAttrs);
        }
        return this;
    }
}

使用示例
HTML结构
<!-- 基础select -->
<select class="form-select" id="category1"></select>

<!-- 带配置的select -->
<select class="form-select"
        data-select-url="/api/categories"
        data-select-value-field="id"
        data-select-text-field="name"
        data-select-placeholder="选择分类">
</select>

<!-- 多个相同配置的select -->
<select class="department-select" data-department-id="1"></select>
<select class="department-select" data-department-id="2"></select>
<select class="department-select" data-department-id="3"></select>

JavaScript使用
$(function() {
    // 创建渲染器实例
    var selectRenderer = new SelectRenderer({
        placeholderText: '请选择',
        valueField: 'id',
        textField: 'name'
    });

    // 示例1：渲染静态数据
    var staticData = [
        {id: '1', name: '技术部'},
        {id: '2', name: '市场部', disabled: true},
        {id: '3', name: '人事部'},
        {id: '4', name: '财务部'}
    ];

    selectRenderer.render('#category1', staticData);

    // 示例2：通过AJAX渲染单个select
    selectRenderer.render('.form-select[data-select-url]', {
        url: '/api/departments',
        dataProcessor: function(response) {
            // 处理返回数据
            return response.list || response.data || [];
        }
    });

    // 示例3：批量渲染相同class的select
    $('.department-select').each(function() {
        var departmentId = $(this).data('department-id');
        var url = '/api/employees?deptId=' + departmentId;

        selectRenderer.render(this, {
            url: url,
            placeholderText: '选择员工'
        });
    });

    // 示例4：使用事件监听
    $('.form-select').on('select:rendered', function() {
        console.log('Select已渲染:', $(this).attr('id'));
    }).on('change', function() {
        console.log('选择的值:', $(this).val());
    });
});

扩展：集成Select2（如果需要增强功能）
/**
 * 集成Select2的渲染器
 */
class Select2Renderer extends SelectRenderer {
    constructor(config = {}) {
        var defaultSelect2Config = {
            theme: 'bootstrap',
            width: '100%',
            allowClear: true,
            placeholder: '请选择'
        };

        super($.extend({
            select2: defaultSelect2Config
        }, config));
    }

    render(selector, data = null, options = {}) {
        var $select = super.render(selector, data, options);

        // 初始化Select2
        if (options.select2 || this.config.select2) {
            var select2Config = $.extend({}, this.config.select2, options.select2);

            // 设置placeholder
            if (options.placeholderText) {
                select2Config.placeholder = options.placeholderText;
            }

            $select.select2(select2Config);

            // 监听销毁事件，防止内存泄漏
            $(window).on('unload', function() {
                $select.select2('destroy');
            });
        }

        return $select;
    }

    setValue($select, value) {
        if ($select.data('select2')) {
            $select.val(value).trigger('change.select2');
        } else {
            super.setValue($select, value);
        }
        return this;
    }
}

// 使用Select2Renderer
$(function() {
    var select2Renderer = new Select2Renderer();

    select2Renderer.render('.select2-enhanced', {
        url: '/api/data',
        select2: {
            theme: 'bootstrap4',
            ajax: {
                url: '/api/search',
                dataType: 'json',
                delay: 250,
                data: function(params) {
                    return {
                        q: params.term,
                        page: params.page
                    };
                }
            }
        }
    });
});

CSS样式建议
/* 加载状态 */
select.loading {
    background-image: url('loading-spinner.svg');
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 16px 16px;
    padding-right: 35px;
}

/* 错误状态 */
select.error {
    border-color: #dc3545;
    background-color: #fff5f5;
}

/* 禁用状态 */
select:disabled {
    background-color: #e9ecef;
    cursor: not-allowed;
}

/* 分组样式 */
optgroup {
    font-weight: bold;
    font-style: normal;
    color: #495057;
}

optgroup option {
    font-weight: normal;
    padding-left: 20px;
}