city-picker.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. layui.define(['jquery'], function (exports) {
  2. var $ = layui.$;
  3. 'use strict';
  4. if (typeof ChineseDistricts === 'undefined') {
  5. throw new Error('The file "city-picker.data.js" must be included first!');
  6. }
  7. var NAMESPACE = 'citypicker';
  8. var EVENT_CHANGE = 'change.' + NAMESPACE;
  9. // 城市选择类
  10. var CityPicker = function (element, options) {
  11. this.PROVINCE = 'provinceId';
  12. this.CITY = 'cityId';
  13. this.DISTRICT = 'districtId';
  14. this.$element = $(element);
  15. this.$dropdown = null;
  16. this.options = $.extend({}, CityPicker.DEFAULTS, $.isPlainObject(options) && options);
  17. this.active = false;
  18. this.dems = [];
  19. this.needBlur = false;
  20. this.init();
  21. };
  22. CityPicker.prototype = {
  23. constructor: CityPicker,
  24. init: function () {
  25. this.defineDems();
  26. this.render();
  27. this.bind();
  28. this.active = true;
  29. var _this = this;
  30. $(".icon_ca").on('click', function () {
  31. $("#" + this.PROVINCE).val();
  32. $("#" + this.CITY).val();
  33. $("#" + this.DISTRICT).val();
  34. _this.reset();
  35. });
  36. },
  37. render: function () {
  38. var p = this.getPosition(),
  39. placeholder = this.$element.attr('placeholder') || this.options.placeholder,
  40. textspan = '<span class="city-picker-span" style="' +
  41. this.getWidthStyle(p.width) + 'height:' +
  42. (p.height - 2) + 'px;line-height:' + (p.height - 1) + 'px;">' +
  43. (placeholder ? '<span class="placeholder">' + placeholder + '</span>' : '') +
  44. '<span class="title"></span><div class="arrow"></div>' + '<i class="icon_ca"></i></span>',
  45. dropdown = '<div class="city-picker-dropdown" style="left:0px;top:100%;' +
  46. this.getWidthStyle(p.width, true) + '">' +
  47. '<div class="city-select-wrap">' +
  48. '<div class="city-select-tab">' +
  49. '<a class="active" data-count="' + this.PROVINCE + '">省份</a>' +
  50. (this.includeDem(this.CITY) ? '<a data-count="' + this.CITY + '">城市</a>' : '') +
  51. (this.includeDem(this.DISTRICT) ? '<a data-count="' + this.DISTRICT + '">区县</a>' : '') + '</div>' +
  52. '<div class="city-select-content">' +
  53. '<div class="city-select province ' + this.PROVINCE + '" data-count="' + this.PROVINCE + '"></div>' +
  54. (this.includeDem(this.CITY) ? '<div class="city-select ' + this.CITY + '" data-count="' + this.CITY + '"></div>' : '') +
  55. (this.includeDem(this.DISTRICT) ? '<div class="city-select ' + this.DISTRICT + '" data-count="' + this.DISTRICT + '"></div>' : '') +
  56. '</div></div>';
  57. this.$element.addClass('city-picker-input');
  58. this.$textspan = $(textspan).insertAfter(this.$element);
  59. this.$dropdown = $(dropdown).insertAfter(this.$textspan);
  60. var $select = this.$dropdown.find('.city-select');
  61. // setup this.$province, this.$city and/or this.$district object
  62. $.each(this.dems, $.proxy(function (i, type) {
  63. this['$' + type] = $select.filter('.' + type + '');
  64. }, this));
  65. this.refresh();
  66. },
  67. refresh: function (force) {
  68. // clean the data-item for each $select
  69. var $select = this.$dropdown.find('.city-select');
  70. $select.data('item', null);
  71. // parse value from value of the target $element
  72. var val = this.$element.val() || '';
  73. val = val.split('/');
  74. $.each(this.dems, $.proxy(function (i, type) {
  75. if (val[i] && i < val.length) {
  76. this.options[type] = val[i];
  77. } else if (force) {
  78. this.options[type] = '';
  79. }
  80. this.output(type);
  81. }, this));
  82. this.tab(this.PROVINCE);
  83. this.feedText();
  84. this.feedVal();
  85. },
  86. defineDems: function () {
  87. var stop = false;
  88. if (this.options.provincename !== "") {
  89. this.PROVINCE = this.options.provincename;
  90. }
  91. if (this.options.cityname !== "") {
  92. this.CITY = this.options.cityname;
  93. }
  94. if (this.options.districtname !== "") {
  95. this.DISTRICT = this.options.districtname;
  96. }
  97. $.each([this.PROVINCE, this.CITY, this.DISTRICT], $.proxy(function (i, type) {
  98. if (!stop) {
  99. this.dems.push(type);
  100. }
  101. if (type === this.options.level) {
  102. stop = true;
  103. }
  104. }, this));
  105. },
  106. includeDem: function (type) {
  107. return $.inArray(type, this.dems) !== -1;
  108. },
  109. getPosition: function () {
  110. var p, h, w, s, pw;
  111. p = this.$element.position();
  112. s = this.getSize(this.$element);
  113. h = s.height;
  114. w = s.width;
  115. if (this.options.responsive) {
  116. pw = this.$element.offsetParent().width();
  117. if (pw) {
  118. w = w / pw;
  119. if (w > 0.99) {
  120. w = 1;
  121. }
  122. w = w * 100 + '%';
  123. }
  124. }
  125. return {
  126. top: p.top || 0,
  127. left: p.left || 0,
  128. height: h,
  129. width: w
  130. };
  131. },
  132. getSize: function ($dom) {
  133. var $wrap, $clone, sizes;
  134. if (!$dom.is(':visible')) {
  135. $wrap = $("<div />").appendTo($("body"));
  136. $wrap.css({
  137. "position": "absolute !important",
  138. "visibility": "hidden !important",
  139. "display": "block !important"
  140. });
  141. $clone = $dom.clone().appendTo($wrap);
  142. sizes = {
  143. width: $clone.outerWidth(),
  144. height: $clone.outerHeight()
  145. };
  146. $wrap.remove();
  147. } else {
  148. sizes = {
  149. width: $dom.outerWidth(),
  150. height: $dom.outerHeight()
  151. };
  152. }
  153. return sizes;
  154. },
  155. getWidthStyle: function (w, dropdown) {
  156. if (this.options.responsive && !$.isNumeric(w)) {
  157. return 'width:' + w + ';';
  158. } else {
  159. return 'width:' + (dropdown ? Math.max(320, w) : w) + 'px;';
  160. }
  161. },
  162. bind: function () {
  163. var $this = this;
  164. $(document).on('click', (this._mouteclick = function (e) {
  165. var $target = $(e.target);
  166. var $dropdown, $span, $input;
  167. if ($target.is('.city-picker-span')) {
  168. $span = $target;
  169. } else if ($target.is('.city-picker-span *')) {
  170. $span = $target.parents('.city-picker-span');
  171. }
  172. if ($target.is('.city-picker-input')) {
  173. $input = $target;
  174. }
  175. if ($target.is('.city-picker-dropdown')) {
  176. $dropdown = $target;
  177. } else if ($target.is('.city-picker-dropdown *')) {
  178. $dropdown = $target.parents('.city-picker-dropdown');
  179. }
  180. if ((!$input && !$span && !$dropdown) ||
  181. ($span && $span.get(0) !== $this.$textspan.get(0)) ||
  182. ($input && $input.get(0) !== $this.$element.get(0)) ||
  183. ($dropdown && $dropdown.get(0) !== $this.$dropdown.get(0))) {
  184. $this.close(true);
  185. }
  186. }));
  187. this.$element.on('change', (this._changeElement = $.proxy(function () {
  188. this.close(true);
  189. this.refresh(true);
  190. }, this))).on('focus', (this._focusElement = $.proxy(function () {
  191. this.needBlur = true;
  192. this.open();
  193. }, this))).on('blur', (this._blurElement = $.proxy(function () {
  194. if (this.needBlur) {
  195. this.needBlur = false;
  196. this.close(true);
  197. }
  198. }, this)));
  199. this.$textspan.on('click', function (e) {
  200. var $target = $(e.target), type;
  201. $this.needBlur = false;
  202. if ($target.is('.select-item')) {
  203. type = $target.data('count');
  204. $this.open(type);
  205. } else {
  206. if ($this.$dropdown.is(':visible')) {
  207. $this.close();
  208. } else {
  209. $this.open();
  210. }
  211. }
  212. }).on('mousedown', function () {
  213. $this.needBlur = false;
  214. });
  215. this.$dropdown.on('click', '.city-select a', function () {
  216. var $select = $(this).parents('.city-select');
  217. var $active = $select.find('a.active');
  218. var last = $select.next().length === 0;
  219. $active.removeClass('active');
  220. $(this).addClass('active');
  221. if ($active.data('code') !== $(this).data('code')) {
  222. $select.data('item', {
  223. address: $(this).attr('title'), code: $(this).data('code')
  224. });
  225. $(this).trigger(EVENT_CHANGE);
  226. $this.feedText();
  227. $this.feedVal();
  228. if (last) {
  229. $this.close();
  230. }
  231. }
  232. }).on('click', '.city-select-tab a', function () {
  233. if (!$(this).hasClass('active')) {
  234. var type = $(this).data('count');
  235. $this.tab(type);
  236. }
  237. }).on('mousedown', function () {
  238. $this.needBlur = false;
  239. });
  240. if (this['$' + this.PROVINCE]) {
  241. this['$' + this.PROVINCE].on(EVENT_CHANGE, (this._changeProvince = $.proxy(function () {
  242. this.output(this.CITY);
  243. this.output(this.DISTRICT);
  244. this.tab(this.CITY);
  245. }, this)));
  246. }
  247. if (this['$' + this.CITY]) {
  248. this['$' + this.CITY].on(EVENT_CHANGE, (this._changeCity = $.proxy(function () {
  249. this.output(this.DISTRICT);
  250. this.tab(this.DISTRICT);
  251. }, this)));
  252. }
  253. },
  254. open: function (type) {
  255. type = type || this.PROVINCE;
  256. this.$dropdown.show();
  257. this.$textspan.addClass('open').addClass('focus');
  258. this.tab(type);
  259. },
  260. close: function (blur) {
  261. this.$dropdown.hide();
  262. this.$textspan.removeClass('open');
  263. if (blur) {
  264. this.$textspan.removeClass('focus');
  265. }
  266. },
  267. unbind: function () {
  268. $(document).off('click', this._mouteclick);
  269. this.$element.off('change', this._changeElement);
  270. this.$element.off('focus', this._focusElement);
  271. this.$element.off('blur', this._blurElement);
  272. this.$textspan.off('click');
  273. this.$textspan.off('mousedown');
  274. this.$dropdown.off('click');
  275. this.$dropdown.off('mousedown');
  276. if (this['$' + this.PROVINCE]) {
  277. this['$' + this.PROVINCE].off(EVENT_CHANGE, this._changeProvince);
  278. }
  279. if (this.$city) {
  280. this.$city.off(EVENT_CHANGE, this._changeCity);
  281. }
  282. },
  283. // 动态添加的input无法删除,切换请求有问题
  284. getText: function () {
  285. var text = '';
  286. this.$dropdown.find('.city-select')
  287. .each(function () {
  288. var item = $(this).data('item'),
  289. type = $(this).data('count');
  290. if (item) {
  291. text += ($(this).hasClass('province') ? '' : '/') + '<span class="select-item" data-count="' +
  292. type + '" data-code="' + item.code + '">' + item.address + '</span><input type="hidden" id="' + type + '" name="' + type + '" value="' + item.code + '" />';
  293. }
  294. });
  295. return text;
  296. },
  297. getPlaceHolder: function () {
  298. return this.$element.attr('placeholder') || this.options.placeholder;
  299. },
  300. feedText: function () {
  301. var text = this.getText();
  302. if (text) {
  303. this.$textspan.find('>.placeholder').hide();
  304. // this.$textspan.find('>.title').siblings('input').val('');
  305. //this.$textspan.find('>.title').children().remove();
  306. this.$textspan.find('>.title').children("input").val('');
  307. this.$textspan.find('>.title').html(this.getText()).show();
  308. } else {
  309. this.$textspan.find('>.placeholder').text(this.getPlaceHolder()).show();
  310. this.$textspan.find('>.title').html('').hide();
  311. }
  312. },
  313. getVal: function () {
  314. var text = '';
  315. this.$dropdown.find('.city-select')
  316. .each(function () {
  317. var item = $(this).data('item');
  318. if (item) {
  319. text += ($(this).hasClass('province') ? '' : '/') + item.address;
  320. }
  321. });
  322. return text;
  323. },
  324. feedVal: function () {
  325. this.$element.val(this.getVal());
  326. },
  327. output: function (type) {
  328. var options = this.options;
  329. var PROVINCE = this.PROVINCE;
  330. var CITY = this.CITY;
  331. var DISTRICT = this.DISTRICT;
  332. //var placeholders = this.placeholders;
  333. var $select = this['$' + type];
  334. var data = type === PROVINCE ? {} : [];
  335. var item;
  336. var districts;
  337. var code;
  338. var matched = null;
  339. var value;
  340. if (!$select || !$select.length) {
  341. return;
  342. }
  343. item = $select.data('item');
  344. value = (item ? item.address : null) || options[type];
  345. code = (
  346. type === PROVINCE ? 86 :
  347. type === CITY ? this['$' + PROVINCE] && this['$' + PROVINCE].find('.active').data('code') :
  348. type === DISTRICT ? this['$' + CITY] && this['$' + CITY].find('.active').data('code') : code
  349. );
  350. districts = $.isNumeric(code) ? ChineseDistricts[code] : null;
  351. if ($.isPlainObject(districts)) {
  352. $.each(districts, function (code, address) {
  353. var provs;
  354. if (type === PROVINCE) {
  355. provs = [];
  356. for (var i = 0; i < address.length; i++) {
  357. if (address[i].address === value) {
  358. matched = {
  359. code: address[i].code,
  360. address: address[i].address
  361. };
  362. }
  363. provs.push({
  364. code: address[i].code,
  365. address: address[i].address,
  366. selected: address[i].address === value
  367. });
  368. }
  369. data[code] = provs;
  370. } else {
  371. if (address === value) {
  372. matched = {
  373. code: code,
  374. address: address
  375. };
  376. }
  377. data.push({
  378. code: code,
  379. address: address,
  380. selected: address === value
  381. });
  382. }
  383. });
  384. }
  385. $select.html(type === PROVINCE ? this.getProvinceList(data) :
  386. this.getList(data, type));
  387. $select.data('item', matched);
  388. },
  389. getProvinceList: function (data) {
  390. var PROVINCE = this.PROVINCE;
  391. var list = [],
  392. $this = this,
  393. simple = this.options.simple;
  394. $.each(data, function (i, n) {
  395. list.push('<dl class="clearfix">');
  396. list.push('<dt>' + i + '</dt><dd>');
  397. $.each(n, function (j, m) {
  398. list.push(
  399. '<a' +
  400. ' title="' + (m.address || '') + '"' +
  401. ' data-code="' + (m.code || '') + '"' +
  402. ' class="' +
  403. (m.selected ? ' active' : '') +
  404. '">' +
  405. (simple ? $this.simplize(m.address, PROVINCE) : m.address) +
  406. '</a>');
  407. });
  408. list.push('</dd></dl>');
  409. });
  410. return list.join('');
  411. },
  412. getList: function (data, type) {
  413. var list = [],
  414. $this = this,
  415. simple = this.options.simple;
  416. list.push('<dl class="clearfix"><dd>');
  417. $.each(data, function (i, n) {
  418. list.push(
  419. '<a' +
  420. ' title="' + (n.address || '') + '"' +
  421. ' data-code="' + (n.code || '') + '"' +
  422. ' class="' +
  423. (n.selected ? ' active' : '') +
  424. '">' +
  425. (simple ? $this.simplize(n.address, type) : n.address) +
  426. '</a>');
  427. });
  428. list.push('</dd></dl>');
  429. return list.join('');
  430. },
  431. simplize: function (address, type) {
  432. address = address || '';
  433. if (type === this.PROVINCE) {
  434. return address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g, '');
  435. } else if (type === this.CITY) {
  436. return address.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g, '')
  437. .replace('哈萨克', '').replace('自治州', '').replace(/自治县/, '');
  438. } else if (type === this.DISTRICT) {
  439. return address.length > 2 ? address.replace(/[市,区,县,旗]/g, '') : address;
  440. }
  441. },
  442. tab: function (type) {
  443. var $selects = this.$dropdown.find('.city-select');
  444. var $tabs = this.$dropdown.find('.city-select-tab > a');
  445. var $select = this['$' + type];
  446. var $tab = this.$dropdown.find('.city-select-tab > a[data-count="' + type + '"]');
  447. if ($select) {
  448. $selects.hide();
  449. $select.show();
  450. $tabs.removeClass('active');
  451. $tab.addClass('active');
  452. }
  453. },
  454. reset: function () {
  455. this.$element.val(null).trigger('change');
  456. },
  457. setValue: function (address) { //河南省/信阳市/新县
  458. this.$element.val(address).trigger('change');
  459. },
  460. destroy: function () {
  461. this.unbind();
  462. this.$element.removeData(NAMESPACE).removeClass('city-picker-input');
  463. this.$textspan.remove();
  464. this.$dropdown.remove();
  465. }
  466. };
  467. // 默认值和参数
  468. CityPicker.DEFAULTS = {
  469. simple: false,
  470. responsive: false,
  471. placeholder: '请选择省/市/区',
  472. level: 'district',// 级别
  473. provincename: '',//input hidden 的值
  474. cityname: '',//input hidden 的值
  475. districtname: '',//input hidden 的值
  476. province: '河南省',// 默认省份名称
  477. city: '',// 默认地市名称
  478. district: ''// 默认区县名称
  479. };
  480. CityPicker.setDefaults = function (options) {
  481. $.extend(CityPicker.DEFAULTS, options);
  482. };
  483. // Save the other citypicker
  484. CityPicker.other = $.fn.citypicker;
  485. // Register as jQuery plugin
  486. $.fn.citypicker = function (option) {
  487. var args = [].slice.call(arguments, 1);
  488. return this.each(function () {
  489. var $this = $(this);
  490. var data = $this.data(NAMESPACE);
  491. var options;
  492. var fn;
  493. if (!data) {
  494. if (/destroy/.test(option)) {
  495. return;
  496. }
  497. options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
  498. $this.data(NAMESPACE, (data = new CityPicker(this, options)));
  499. }
  500. if (typeof option === 'string' && $.isFunction(fn = data[option])) {
  501. fn.apply(data, args);
  502. }
  503. });
  504. };
  505. $.fn.citypicker.Constructor = CityPicker;
  506. $.fn.citypicker.setDefaults = CityPicker.setDefaults;
  507. // No conflict
  508. $.fn.citypicker.noConflict = function () {
  509. $.fn.citypicker = CityPicker.other;
  510. return this;
  511. };
  512. // 自动扫描机制注释掉
  513. //$(function () {
  514. // $('[data-toggle="city-picker"]').citypicker();
  515. //});
  516. exports('citypicker', CityPicker);
  517. });