使用 jQuery 根据类选择器渲染 select 说明.txt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. js SelectRenderer封装类
  2. /**
  3. * Select渲染器
  4. */
  5. class SelectRenderer {
  6. constructor(config = {}) {
  7. this.config = $.extend({
  8. placeholder: true,
  9. placeholderText: '请选择',
  10. allowClear: false,
  11. searchable: false,
  12. ajax: false,
  13. data: [],
  14. valueField: 'value',
  15. textField: 'text',
  16. disabledField: 'disabled',
  17. selectedField: 'selected',
  18. groupField: 'group'
  19. }, config);
  20. }
  21. /**
  22. * 渲染单个select
  23. * @param {jQuery|string} selector - 选择器或jQuery对象
  24. * @param {Array|Object} data - 数据
  25. * @param {Object} options - 选项
  26. */
  27. render(selector, data = null, options = {}) {
  28. var $select = $(selector);
  29. if (!$select.length) return;
  30. var config = $.extend({}, this.config, options);
  31. var renderData = data || config.data;
  32. // 清空现有选项
  33. $select.empty();
  34. // 添加占位符
  35. if (config.placeholder) {
  36. var $placeholder = $('<option>', {
  37. value: '',
  38. text: config.placeholderText
  39. });
  40. if (config.allowClear) {
  41. $placeholder.attr('data-allow-clear', 'true');
  42. }
  43. $select.append($placeholder);
  44. }
  45. // 渲染选项
  46. if ($.isArray(renderData)) {
  47. this._renderOptions($select, renderData, config);
  48. } else if (renderData && renderData.url) {
  49. this._renderWithAjax($select, renderData, config);
  50. }
  51. // 触发渲染完成事件
  52. $select.trigger('select:rendered');
  53. return $select;
  54. }
  55. /**
  56. * 渲染多个select
  57. * @param {string} className - 类选择器
  58. * @param {Array} data - 数据
  59. * @param {Object} options - 选项
  60. */
  61. renderAll(className, data = null, options = {}) {
  62. var self = this;
  63. var results = [];
  64. $(className).each(function() {
  65. var $select = $(this);
  66. var specificOptions = $.extend({}, options);
  67. // 从data-*属性读取配置
  68. var dataAttrs = self._getDataAttributes($select);
  69. $.extend(specificOptions, dataAttrs);
  70. results.push(self.render($select, data, specificOptions));
  71. });
  72. return results;
  73. }
  74. /**
  75. * 内部方法:渲染选项
  76. */
  77. _renderOptions($select, data, config) {
  78. var groups = {};
  79. $.each(data, function(index, item) {
  80. // 分组处理
  81. if (config.groupField && item[config.groupField]) {
  82. var groupName = item[config.groupField];
  83. if (!groups[groupName]) {
  84. groups[groupName] = [];
  85. }
  86. groups[groupName].push(item);
  87. } else {
  88. var $option = $('<option>', {
  89. value: item[config.valueField] || item.value || '',
  90. text: item[config.textField] || item.text || '',
  91. disabled: item[config.disabledField] || item.disabled || false,
  92. selected: item[config.selectedField] || item.selected || false
  93. });
  94. // 添加自定义属性
  95. $.each(item, function(key, value) {
  96. if (!['value', 'text', 'disabled', 'selected'].includes(key)) {
  97. $option.attr('data-' + key, value);
  98. }
  99. });
  100. $select.append($option);
  101. }
  102. });
  103. // 渲染分组
  104. this._renderOptionGroups($select, groups, config);
  105. }
  106. /**
  107. * 内部方法:渲染分组
  108. */
  109. _renderOptionGroups($select, groups, config) {
  110. $.each(groups, function(groupName, items) {
  111. var $optgroup = $('<optgroup>', {
  112. label: groupName
  113. });
  114. $.each(items, function(index, item) {
  115. var $option = $('<option>', {
  116. value: item[config.valueField] || item.value || '',
  117. text: item[config.textField] || item.text || '',
  118. disabled: item[config.disabledField] || item.disabled || false,
  119. selected: item[config.selectedField] || item.selected || false
  120. });
  121. $optgroup.append($option);
  122. });
  123. $select.append($optgroup);
  124. });
  125. }
  126. /**
  127. * 内部方法:通过AJAX渲染
  128. */
  129. _renderWithAjax($select, ajaxConfig, config) {
  130. var self = this;
  131. // 显示加载状态
  132. $select.prop('disabled', true).addClass('loading');
  133. $.ajax($.extend({
  134. type: 'GET',
  135. dataType: 'json',
  136. success: function(response) {
  137. // 处理响应数据
  138. var data = response;
  139. if (ajaxConfig.dataProcessor && typeof ajaxConfig.dataProcessor === 'function') {
  140. data = ajaxConfig.dataProcessor(response);
  141. } else if (response.data) {
  142. data = response.data;
  143. }
  144. // 渲染数据
  145. self._renderOptions($select, data, config);
  146. // 恢复select状态
  147. $select.prop('disabled', false).removeClass('loading');
  148. // 触发完成事件
  149. $select.trigger('select:ajax:complete', [response]);
  150. },
  151. error: function(xhr, status, error) {
  152. console.error('加载下拉框数据失败:', error);
  153. // 添加错误选项
  154. $select.empty();
  155. $select.append($('<option>', {
  156. value: '',
  157. text: '数据加载失败',
  158. disabled: true
  159. }));
  160. // 恢复状态
  161. $select.prop('disabled', false).removeClass('loading');
  162. // 触发错误事件
  163. $select.trigger('select:ajax:error', [xhr, status, error]);
  164. }
  165. }, ajaxConfig));
  166. }
  167. /**
  168. * 获取data-*属性
  169. */
  170. _getDataAttributes($element) {
  171. var attrs = {};
  172. var data = $element.data();
  173. // 转换属性名
  174. $.each(data, function(key, value) {
  175. if (key.startsWith('select')) {
  176. var newKey = key.replace(/^select/, '');
  177. newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
  178. attrs[newKey] = value;
  179. }
  180. });
  181. return attrs;
  182. }
  183. /**
  184. * 设置选中值
  185. */
  186. setValue($select, value) {
  187. $select.val(value).trigger('change');
  188. return this;
  189. }
  190. /**
  191. * 启用/禁用
  192. */
  193. setDisabled($select, disabled) {
  194. $select.prop('disabled', disabled);
  195. return this;
  196. }
  197. /**
  198. * 重新加载数据
  199. */
  200. reload($select) {
  201. var dataAttrs = this._getDataAttributes($select);
  202. if (dataAttrs.url) {
  203. this._renderWithAjax($select, { url: dataAttrs.url }, dataAttrs);
  204. }
  205. return this;
  206. }
  207. }
  208. 使用示例
  209. HTML结构
  210. <!-- 基础select -->
  211. <select class="form-select" id="category1"></select>
  212. <!-- 带配置的select -->
  213. <select class="form-select"
  214. data-select-url="/api/categories"
  215. data-select-value-field="id"
  216. data-select-text-field="name"
  217. data-select-placeholder="选择分类">
  218. </select>
  219. <!-- 多个相同配置的select -->
  220. <select class="department-select" data-department-id="1"></select>
  221. <select class="department-select" data-department-id="2"></select>
  222. <select class="department-select" data-department-id="3"></select>
  223. JavaScript使用
  224. $(function() {
  225. // 创建渲染器实例
  226. var selectRenderer = new SelectRenderer({
  227. placeholderText: '请选择',
  228. valueField: 'id',
  229. textField: 'name'
  230. });
  231. // 示例1:渲染静态数据
  232. var staticData = [
  233. {id: '1', name: '技术部'},
  234. {id: '2', name: '市场部', disabled: true},
  235. {id: '3', name: '人事部'},
  236. {id: '4', name: '财务部'}
  237. ];
  238. selectRenderer.render('#category1', staticData);
  239. // 示例2:通过AJAX渲染单个select
  240. selectRenderer.render('.form-select[data-select-url]', {
  241. url: '/api/departments',
  242. dataProcessor: function(response) {
  243. // 处理返回数据
  244. return response.list || response.data || [];
  245. }
  246. });
  247. // 示例3:批量渲染相同class的select
  248. $('.department-select').each(function() {
  249. var departmentId = $(this).data('department-id');
  250. var url = '/api/employees?deptId=' + departmentId;
  251. selectRenderer.render(this, {
  252. url: url,
  253. placeholderText: '选择员工'
  254. });
  255. });
  256. // 示例4:使用事件监听
  257. $('.form-select').on('select:rendered', function() {
  258. console.log('Select已渲染:', $(this).attr('id'));
  259. }).on('change', function() {
  260. console.log('选择的值:', $(this).val());
  261. });
  262. });
  263. 扩展:集成Select2(如果需要增强功能)
  264. /**
  265. * 集成Select2的渲染器
  266. */
  267. class Select2Renderer extends SelectRenderer {
  268. constructor(config = {}) {
  269. var defaultSelect2Config = {
  270. theme: 'bootstrap',
  271. width: '100%',
  272. allowClear: true,
  273. placeholder: '请选择'
  274. };
  275. super($.extend({
  276. select2: defaultSelect2Config
  277. }, config));
  278. }
  279. render(selector, data = null, options = {}) {
  280. var $select = super.render(selector, data, options);
  281. // 初始化Select2
  282. if (options.select2 || this.config.select2) {
  283. var select2Config = $.extend({}, this.config.select2, options.select2);
  284. // 设置placeholder
  285. if (options.placeholderText) {
  286. select2Config.placeholder = options.placeholderText;
  287. }
  288. $select.select2(select2Config);
  289. // 监听销毁事件,防止内存泄漏
  290. $(window).on('unload', function() {
  291. $select.select2('destroy');
  292. });
  293. }
  294. return $select;
  295. }
  296. setValue($select, value) {
  297. if ($select.data('select2')) {
  298. $select.val(value).trigger('change.select2');
  299. } else {
  300. super.setValue($select, value);
  301. }
  302. return this;
  303. }
  304. }
  305. // 使用Select2Renderer
  306. $(function() {
  307. var select2Renderer = new Select2Renderer();
  308. select2Renderer.render('.select2-enhanced', {
  309. url: '/api/data',
  310. select2: {
  311. theme: 'bootstrap4',
  312. ajax: {
  313. url: '/api/search',
  314. dataType: 'json',
  315. delay: 250,
  316. data: function(params) {
  317. return {
  318. q: params.term,
  319. page: params.page
  320. };
  321. }
  322. }
  323. }
  324. });
  325. });
  326. CSS样式建议
  327. /* 加载状态 */
  328. select.loading {
  329. background-image: url('loading-spinner.svg');
  330. background-repeat: no-repeat;
  331. background-position: right 10px center;
  332. background-size: 16px 16px;
  333. padding-right: 35px;
  334. }
  335. /* 错误状态 */
  336. select.error {
  337. border-color: #dc3545;
  338. background-color: #fff5f5;
  339. }
  340. /* 禁用状态 */
  341. select:disabled {
  342. background-color: #e9ecef;
  343. cursor: not-allowed;
  344. }
  345. /* 分组样式 */
  346. optgroup {
  347. font-weight: bold;
  348. font-style: normal;
  349. color: #495057;
  350. }
  351. optgroup option {
  352. font-weight: normal;
  353. padding-left: 20px;
  354. }