Showing
1 changed file
with
279 additions
and
0 deletions
public/js/plugins/cascading-address.js
0 → 100644
1 | +/** | ||
2 | + * 区域城市级联选择 | ||
3 | + * @author: dongjinhu(jinhu.dong@yoho.cn) | ||
4 | + * @date: 2016/06/30 | ||
5 | + * @desc: | ||
6 | + * 1. 为了减少库的复杂度,应用层的地址接口应该统一并且固定 | ||
7 | + * @examle: | ||
8 | + * CascadingAddress({ | ||
9 | + * el: '#address', | ||
10 | + * resources: 'areas', | ||
11 | + * url: '', // 初始化数据请求 | ||
12 | + * onCreated: function () {} // 初始化完成回调 | ||
13 | + * }) | ||
14 | + */ | ||
15 | + | ||
16 | +var $ = require('yoho-jquery'); | ||
17 | + | ||
18 | +// 默认配置 | ||
19 | +var settings = { | ||
20 | + url: '', | ||
21 | + containerClass: 'cascading-address', | ||
22 | + levels: 3, | ||
23 | + resources: 'areas', | ||
24 | + names: ['province', 'city', 'district'], | ||
25 | + indicators: ['请选择省份', '请选择城市', '请选择区/县'], | ||
26 | + defaultDes: '加载中...', | ||
27 | + ajaxSettings: { | ||
28 | + type: 'GET', | ||
29 | + url: '', | ||
30 | + contentType: 'application/json; charset=utf-8', | ||
31 | + dataType: 'json' | ||
32 | + } | ||
33 | +}; | ||
34 | + | ||
35 | +// 显示或隐藏下拉区域 | ||
36 | +function toggleDistItem() { | ||
37 | + var $this = $(this), | ||
38 | + resetHeight = $this.css('height'), | ||
39 | + openHeight = (parseInt(resetHeight.match(/\d*/)[0], 10) + 5) + 'px', | ||
40 | + openAndResetHeight = (parseInt(resetHeight.match(/\d*/)[0], 10) - 5) + 'px'; | ||
41 | + | ||
42 | + if ($this.hasClass('closed')) { | ||
43 | + // 如果有其他开着,先关掉 | ||
44 | + $('.items-indicator .open').each(function() { | ||
45 | + $(this).find('.iconfont').replaceWith('<span class="iconfont up"></span>') | ||
46 | + .end() | ||
47 | + .removeClass('open') | ||
48 | + .addClass('closed') | ||
49 | + .css({ | ||
50 | + height: resetHeight | ||
51 | + }).next().hide(); | ||
52 | + }); | ||
53 | + | ||
54 | + // open | ||
55 | + $this.find('.iconfont').replaceWith('<span class="iconfont up"></span>') | ||
56 | + .end() | ||
57 | + .removeClass('closed') | ||
58 | + .addClass('open') | ||
59 | + .css({ | ||
60 | + height: openHeight | ||
61 | + }) | ||
62 | + .next() | ||
63 | + .show(); | ||
64 | + | ||
65 | + // 有视觉差 | ||
66 | + // setTimeout(function(){ | ||
67 | + // $('.items-panel').slideDown('fast'); | ||
68 | + // }, 300) | ||
69 | + } else { | ||
70 | + // close | ||
71 | + $this.find('.iconfont').replaceWith('<span class="iconfont up"></span>') | ||
72 | + .end() | ||
73 | + .removeClass('open') | ||
74 | + .addClass('closed') | ||
75 | + .css({ | ||
76 | + height: openAndResetHeight | ||
77 | + }).next().hide(); | ||
78 | + } | ||
79 | +} | ||
80 | + | ||
81 | +// 重置下一级文本显示和值 | ||
82 | +function resetNextLevel(level, config) { | ||
83 | + $('#level_' + level + '_panel').empty() | ||
84 | + .addClass('empty') | ||
85 | + .text(config.defaultDes) | ||
86 | + .prev().find('.indicator-name') | ||
87 | + .text(config.indicators[config.levels - level - 1]) | ||
88 | + .next().val(''); | ||
89 | +} | ||
90 | + | ||
91 | +// 获取初始化数据 | ||
92 | +function fetchInitialData(evt, config, level) { | ||
93 | + var distItem, // 请选择... | ||
94 | + distItemAreaId = $('.items-indicator .open input').val(), | ||
95 | + ajaxSettings = $.extend({}, config.ajaxSettings, { | ||
96 | + url: config.url | ||
97 | + }); | ||
98 | + | ||
99 | + var ulElement = $('<ul/>'), | ||
100 | + liElement, | ||
101 | + distElement; | ||
102 | + | ||
103 | + var urlArr; | ||
104 | + | ||
105 | + // 当前选中区域id | ||
106 | + var currAreaId, | ||
107 | + currPanel; | ||
108 | + | ||
109 | + $.ajax(ajaxSettings).done(function(res) { | ||
110 | + // 渲染后台区域信息列表 | ||
111 | + $(res.data).each(function(index, item) { | ||
112 | + liElement = $('<li/>'); | ||
113 | + | ||
114 | + $('<span class="check-icon iconfont"></span>').appendTo(liElement); | ||
115 | + $('<span/>', { | ||
116 | + class: 'dist-name', | ||
117 | + title: item.text, | ||
118 | + text: item.text, | ||
119 | + id: 'area_' + item.value | ||
120 | + }).appendTo(liElement); | ||
121 | + | ||
122 | + liElement.appendTo(ulElement); | ||
123 | + }); | ||
124 | + | ||
125 | + // 填充下拉 | ||
126 | + if (level >= 0) { | ||
127 | + currPanel = $('#level_' + level + '_panel'); | ||
128 | + ulElement.appendTo($(config.el).find('#level_' + level + '_panel').empty().removeClass('empty')); | ||
129 | + } | ||
130 | + | ||
131 | + // 选择区域 | ||
132 | + currPanel.off('click', '.dist-name').on('click', '.dist-name', function() { | ||
133 | + distElement = $(this); | ||
134 | + currAreaId = distElement.attr('id').split('_')[1]; | ||
135 | + distItem = $('#level_' + level + '_panel').prev(); | ||
136 | + | ||
137 | + if (distElement.hasClass('checked')) { | ||
138 | + // TODO | ||
139 | + // distElement.removeClass('checked'); | ||
140 | + } else { | ||
141 | + currPanel.find('span.checked').removeClass('checked').prev().hide(); | ||
142 | + distElement.addClass('checked').prev().show(); | ||
143 | + distItem.find('.indicator-name') | ||
144 | + .html(distElement.text()) | ||
145 | + .next() | ||
146 | + .val(currAreaId); | ||
147 | + | ||
148 | + // 填充下一级 | ||
149 | + if (level < 0) { | ||
150 | + level = distElement.parents('.items-panel').attr('id').split('_')[1]; | ||
151 | + } | ||
152 | + | ||
153 | + urlArr = config.url.split(config.resources + '/'); | ||
154 | + urlArr[1] = currAreaId; | ||
155 | + config.url = urlArr.join(config.resources + '/'); | ||
156 | + | ||
157 | + // 选择之后关闭当前panel | ||
158 | + // distItem.trigger('click'); | ||
159 | + | ||
160 | + // 当前点击的和已选择的不同,再渲染下一级目录 | ||
161 | + if (level && distItemAreaId !== currAreaId) { | ||
162 | + resetNextLevel(level - 1, config); | ||
163 | + $(config.el).trigger('ca.afterInit', [config, level - 1]); | ||
164 | + } | ||
165 | + } | ||
166 | + }); | ||
167 | + }).fail(function() { | ||
168 | + // 如果出错,将下级所有都重置 | ||
169 | + var from = level; | ||
170 | + | ||
171 | + while (from >= 0) { | ||
172 | + resetNextLevel(from, config); | ||
173 | + from--; | ||
174 | + } | ||
175 | + }); | ||
176 | +} | ||
177 | + | ||
178 | +// 构造器 | ||
179 | +function CascadingAddress(options) { | ||
180 | + var widget = this, | ||
181 | + element; | ||
182 | + | ||
183 | + widget.config = $.extend(true, {}, settings, options); // Object.assign({}, settings, options); | ||
184 | + element = widget.element = $(widget.config.el); | ||
185 | + | ||
186 | + element.on('ca.afterInit', fetchInitialData); | ||
187 | + element.on('click', '.dist-item', toggleDistItem); | ||
188 | + | ||
189 | + // 绑定自定义事件处理 | ||
190 | + $.each(widget.config, function(key, value) { | ||
191 | + if ($.isFunction(value)) { | ||
192 | + element.on('ca.' + key, function(evt, params) { | ||
193 | + // 处理自定义事件 | ||
194 | + return value(evt, element, params); | ||
195 | + }); | ||
196 | + } | ||
197 | + }); | ||
198 | + | ||
199 | + // 初始化 | ||
200 | + this.init(); | ||
201 | +} | ||
202 | + | ||
203 | +// 初始化 | ||
204 | +CascadingAddress.prototype.init = function() { | ||
205 | + // 配置 | ||
206 | + var config = this.config, | ||
207 | + el = this.element, | ||
208 | + target = el.addClass(config.containerClass), | ||
209 | + levels = config.levels; | ||
210 | + | ||
211 | + // DOM相关 | ||
212 | + var names = config.names, | ||
213 | + indicators = config.indicators, | ||
214 | + distItem, | ||
215 | + inputName, | ||
216 | + indicatorName; | ||
217 | + | ||
218 | + // 省份,城市,区县容器 | ||
219 | + var distItemContainer = $('<ul class="items-indicator"/>'), | ||
220 | + liElement, | ||
221 | + distPanel; | ||
222 | + | ||
223 | + // ======生成DOM结构====== | ||
224 | + while (levels--) { | ||
225 | + indicatorName = indicators[config.levels - levels - 1]; | ||
226 | + | ||
227 | + // li element | ||
228 | + liElement = $('<li/>'); | ||
229 | + | ||
230 | + // div element | ||
231 | + distItem = $('<div></div>', { | ||
232 | + class: 'dist-item closed' | ||
233 | + }); | ||
234 | + | ||
235 | + // 标签 | ||
236 | + $('<span/>', { | ||
237 | + class: 'indicator-name', | ||
238 | + text: indicatorName | ||
239 | + }).appendTo(distItem); | ||
240 | + | ||
241 | + // input element | ||
242 | + inputName = names[config.levels - levels - 1]; | ||
243 | + $('<input/>', { | ||
244 | + type: 'hidden', | ||
245 | + name: inputName, | ||
246 | + id: inputName, | ||
247 | + value: '' | ||
248 | + }).appendTo(distItem); | ||
249 | + | ||
250 | + // icon element | ||
251 | + $('<span class="iconfont"></span>').appendTo(distItem); | ||
252 | + | ||
253 | + distItem.appendTo(liElement); | ||
254 | + | ||
255 | + // 省份城市区县选择panel | ||
256 | + distPanel = $('<div/>', { | ||
257 | + class: 'items-panel empty', | ||
258 | + id: 'level_' + levels + '_panel', | ||
259 | + text: config.defaultDes | ||
260 | + }); | ||
261 | + distPanel.appendTo(liElement); | ||
262 | + | ||
263 | + liElement.appendTo(distItemContainer); | ||
264 | + } | ||
265 | + | ||
266 | + distItemContainer.appendTo(target); | ||
267 | + | ||
268 | + // ======生成DOM结构====== | ||
269 | + | ||
270 | + // 获取初始化数据 | ||
271 | + el.trigger('ca.afterInit', [config, config.levels - 1]); | ||
272 | + | ||
273 | + // 初始化完成,提供回调执行 | ||
274 | + el.trigger('ca.onCreated'); | ||
275 | +}; | ||
276 | + | ||
277 | +module.exports = function(options) { | ||
278 | + return new CascadingAddress(options); | ||
279 | +}; |
-
Please register or login to post a comment