Authored by jinhu.tung

add cascading-address component

... ... @@ -130,5 +130,10 @@
</div>
</ul>
</div>
<p style="margin-top: 20px;">14. 区域/选择地址组件</p>
<div style="padding: 5px; height: 300px;">
<div id="address"></div>
</div>
{{/ content}}
</div>
\ No newline at end of file
... ...
### 测试数据
- 使用目的
为了快速构建组件和业务模型,模拟服务端的接口返回,首先集中开发前端。
- 如何使用
全局安装json-server
npm i -g json-server
启动
json-server --watch mock/xx.json
xx.json就是需要模拟服务端返回的json数据
\ No newline at end of file
... ...
{
"areas": [
{"id": 0, "data": [
{"value":"11","text":"\u5317\u4eac\u5e02","is_support":"Y"},
{"value":"12","text":"\u5929\u6d25\u5e02","is_support":"Y"},
{"value":"13","text":"\u6cb3\u5317\u7701","is_support":"Y"},
{"value":"14","text":"\u5c71\u897f\u7701","is_support":"Y"},
{"value":"15","text":"\u5185\u8499\u53e4\u81ea\u6cbb\u533a","is_support":"Y"},
{"value":"21","text":"\u8fbd\u5b81\u7701","is_support":"Y"},
{"value":"22","text":"\u5409\u6797\u7701","is_support":"Y"},
{"value":"23","text":"\u9ed1\u9f99\u6c5f\u7701","is_support":"Y"},
{"value":"31","text":"\u4e0a\u6d77\u5e02","is_support":"Y"},
{"value":"32","text":"\u6c5f\u82cf\u7701","is_support":"N"},
{"value":"33","text":"\u6d59\u6c5f\u7701","is_support":"Y"},
{"value":"34","text":"\u5b89\u5fbd\u7701","is_support":"Y"},
{"value":"35","text":"\u798f\u5efa\u7701","is_support":"N"},
{"value":"36","text":"\u6c5f\u897f\u7701","is_support":"N"},
{"value":"37","text":"\u5c71\u4e1c\u7701","is_support":"N"},
{"value":"41","text":"\u6cb3\u5357\u7701","is_support":"N"},
{"value":"42","text":"\u6e56\u5317\u7701","is_support":"N"},
{"value":"43","text":"\u6e56\u5357\u7701","is_support":"Y"},
{"value":"44","text":"\u5e7f\u4e1c\u7701","is_support":"Y"},
{"value":"45","text":"\u5e7f\u897f\u58ee\u65cf\u81ea\u6cbb\u533a","is_support":"Y"},
{"value":"46","text":"\u6d77\u5357\u7701","is_support":"Y"},
{"value":"50","text":"\u91cd\u5e86\u5e02","is_support":"Y"},
{"value":"51","text":"\u56db\u5ddd\u7701","is_support":"Y"},
{"value":"52","text":"\u8d35\u5dde\u7701","is_support":"Y"},
{"value":"53","text":"\u4e91\u5357\u7701","is_support":"N"},
{"value":"54","text":"\u897f\u85cf\u81ea\u6cbb\u533a","is_support":"Y"},
{"value":"61","text":"\u9655\u897f\u7701","is_support":"Y"},
{"value":"62","text":"\u7518\u8083\u7701","is_support":"Y"},
{"value":"63","text":"\u9752\u6d77\u7701","is_support":"Y"},
{"value":"64","text":"\u5b81\u590f\u56de\u65cf\u81ea\u6cbb\u533a","is_support":"Y"},
{"value":"65","text":"\u65b0\u7586\u7ef4\u543e\u5c14\u81ea\u6cbb\u533a","is_support":"N"}]
},{
"id": 32, "data": [
{"value":"3201","text":"\u5357\u4eac\u5e02","is_support":"Y"},
{"value":"3202","text":"\u65e0\u9521\u5e02","is_support":"N"},
{"value":"3203","text":"\u5f90\u5dde\u5e02","is_support":"N"},
{"value":"3204","text":"\u5e38\u5dde\u5e02","is_support":"N"},
{"value":"3205","text":"\u82cf\u5dde\u5e02","is_support":"N"},
{"value":"3206","text":"\u5357\u901a\u5e02","is_support":"N"},
{"value":"3207","text":"\u8fde\u4e91\u6e2f\u5e02","is_support":"N"},
{"value":"3208","text":"\u6dee\u5b89\u5e02","is_support":"N"},
{"value":"3209","text":"\u76d0\u57ce\u5e02","is_support":"N"},
{"value":"3210","text":"\u626c\u5dde\u5e02","is_support":"N"},
{"value":"3211","text":"\u9547\u6c5f\u5e02","is_support":"N"},
{"value":"3212","text":"\u6cf0\u5dde\u5e02","is_support":"N"},
{"value":"3213","text":"\u5bbf\u8fc1\u5e02","is_support":"N"}]
}, {
"id": 3201, "data": [{"value":"320102","text":"\u7384\u6b66\u533a","is_support":"Y"},
{"value":"320103","text":"\u767d\u4e0b\u533a","is_support":"Y"},
{"value":"320104","text":"\u79e6\u6dee\u533a","is_support":"Y"},
{"value":"320105","text":"\u5efa\u90ba\u533a","is_support":"Y"},
{"value":"320106","text":"\u9f13\u697c\u533a","is_support":"Y"},
{"value":"320107","text":"\u4e0b\u5173\u533a","is_support":"Y"},
{"value":"320111","text":"\u6d66\u53e3\u533a","is_support":"Y"},
{"value":"320113","text":"\u6816\u971e\u533a","is_support":"Y"},
{"value":"320114","text":"\u96e8\u82b1\u53f0\u533a","is_support":"Y"},
{"value":"320115","text":"\u6c5f\u5b81\u533a","is_support":"Y"},
{"value":"320116","text":"\u516d\u5408\u533a","is_support":"Y"},
{"value":"320124","text":"\u6ea7\u6c34\u53bf","is_support":"Y"},
{"value":"320125","text":"\u9ad8\u6df3\u53bf","is_support":"Y"}]
}
]
}
\ No newline at end of file
... ...
... ... @@ -6,6 +6,8 @@ var _dialog = dialog.Dialog,
_alert = dialog.Alert,
_confirm = dialog.Confirm;
var cascadingAddress = require('../plugins/cascading-address');
$('.alert-btn').click(function() {
new _alert('购买成功<br>进入 个人中心>我的订单<br>查看门票信息').show();
});
... ... @@ -45,3 +47,15 @@ $('.dialog-btn').click(function() {
});
lazyLoad($('img.lazy'));
$(function() {
// 运行此demo
// 1. 安装 npm i -g json-server
// 2. json-server --watch mock/address.json
cascadingAddress({
el: '#address',
url: 'http://localhost:3000/areas/0',
resource: 'areas'
});
});
... ...
/**
* 区域城市级联选择
* @author: dongjinhu(jinhu.dong@yoho.cn)
* @date: 2016/06/30
* @desc:
* 1. 为了减少库的复杂度,应用层的地址接口应该统一并且固定
* @examle:
* CascadingAddress({
* el: '#address',
* resources: 'areas',
* url: '', // 初始化数据请求
* onCreated: function () {} // 初始化完成回调
* })
*/
var $ = require('yoho-jquery');
// 默认配置
var settings = {
url: '',
containerClass: 'cascading-address',
levels: 3,
resources: 'areas',
names: ['province', 'city', 'district'],
indicators: ['请选择省份', '请选择城市', '请选择区/县'],
defaultDes: '加载中...',
ajaxSettings: {
type: 'GET',
url: '',
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}
};
// 显示或隐藏下拉区域
function toggleDistItem() {
var $this = $(this),
resetHeight = $this.css('height'),
openHeight = (parseInt(resetHeight.match(/\d*/)[0], 10) + 5) + 'px',
openAndResetHeight = (parseInt(resetHeight.match(/\d*/)[0], 10) - 5) + 'px';
if ($this.hasClass('closed')) {
// 如果有其他开着,先关掉
$('.items-indicator .open').each(function() {
$(this).find('.iconfont').replaceWith('<span class="iconfont up">&#xe60a;</span>')
.end()
.removeClass('open')
.addClass('closed')
.css({
height: resetHeight
}).next().hide();
});
// open
$this.find('.iconfont').replaceWith('<span class="iconfont up">&#xe609;</span>')
.end()
.removeClass('closed')
.addClass('open')
.css({
height: openHeight
})
.next()
.show();
// 有视觉差
// setTimeout(function(){
// $('.items-panel').slideDown('fast');
// }, 300)
} else {
// close
$this.find('.iconfont').replaceWith('<span class="iconfont up">&#xe60a;</span>')
.end()
.removeClass('open')
.addClass('closed')
.css({
height: openAndResetHeight
}).next().hide();
}
}
// 获取初始化数据
function fetchInitialData(evt, config, level) {
var distItem, // 请选择...
distItemAreaId = $('.items-indicator .open input').val(),
ajaxSettings = $.extend({}, config.ajaxSettings, {
url: config.url
});
var ulElement = $('<ul/>'),
liElement,
aElement;
var urlArr;
// 当前选中区域id
var currAreaId,
currPanel;
$.ajax(ajaxSettings).done(function(res) {
// 渲染后台区域信息列表
$(res.data).each(function(index, item) {
liElement = $('<li/>');
$('<span class="check-icon iconfont">&#xe60b;</span>').appendTo(liElement);
$('<a/>', {
href: 'javascript:void(0);',
title: item.text,
text: item.text,
id: 'area_' + item.value
}).appendTo(liElement);
liElement.appendTo(ulElement);
});
// 填充下拉
if (level >= 0) {
currPanel = $('#level_' + level + '_panel');
ulElement.appendTo($(config.el).find('#level_' + level + '_panel').empty().removeClass('empty'));
}
// 选择区域
currPanel.off('click', 'a').on('click', 'a', function() {
aElement = $(this);
currAreaId = aElement.attr('id').split('_')[1];
distItem = $('#level_' + level + '_panel').prev();
if (aElement.hasClass('checked')) {
// TODO
// aElement.removeClass('checked');
} else {
currPanel.find('a.checked').removeClass('checked').prev().hide();
aElement.addClass('checked').prev().show();
distItem.find('.indicator-name')
.html(aElement.text())
.next()
.val(currAreaId);
// 填充下一级
if (level < 0) {
level = aElement.parents('.items-panel').attr('id').split('_')[1];
}
urlArr = config.url.split(config.resources + '/');
urlArr[1] = currAreaId;
config.url = urlArr.join(config.resources + '/');
// 选择之后关闭当前panel
// distItem.trigger('click');
// 当前点击的和已选择的不同,再渲染下一级目录
if (level && distItemAreaId !== currAreaId) {
$(config.el).trigger('ca.afterInit', [config, level - 1]);
}
}
});
}).fail(function() {
// 如果出错,将下级所有都重置
var from = level;
while (from >= 0) {
$('#level_' + from + '_panel').empty()
.addClass('empty')
.text(config.defaultDes)
.prev().find('.indicator-name')
.text(config.indicators[config.levels - from - 1]);
from--;
}
});
}
// 构造器
function CascadingAddress(options) {
var widget = this,
element;
widget.config = $.extend(true, {}, settings, options); // Object.assign({}, settings, options);
element = widget.element = $(widget.config.el);
element.on('ca.afterInit', fetchInitialData);
element.on('click', '.dist-item', toggleDistItem);
// 绑定自定义事件处理
$.each(widget.config, function(key, value) {
if ($.isFunction(value)) {
element.on('ca.' + key, function(evt, params) {
// 处理自定义事件
return value(evt, element, params);
});
}
});
// 初始化
this.init();
}
// 初始化
CascadingAddress.prototype.init = function() {
// 配置
var config = this.config,
el = this.element,
target = el.addClass(config.containerClass),
levels = config.levels;
// DOM相关
var names = config.names,
indicators = config.indicators,
distItem,
inputName,
indicatorName;
// 省份,城市,区县容器
var distItemContainer = $('<ul class="items-indicator"/>'),
liElement,
distPanel;
// ======生成DOM结构======
while (levels--) {
indicatorName = indicators[config.levels - levels - 1];
// li element
liElement = $('<li/>');
// div element
distItem = $('<div></div>', {
class: 'dist-item closed'
});
// 标签
$('<span/>', {
class: 'indicator-name',
text: indicatorName
}).appendTo(distItem);
// input element
inputName = names[config.levels - levels - 1];
$('<input/>', {
type: 'hidden',
name: inputName,
id: inputName,
value: ''
}).appendTo(distItem);
// icon element
$('<span class="iconfont">&#xe60a;</span>').appendTo(distItem);
distItem.appendTo(liElement);
// 省份城市区县选择panel
distPanel = $('<div/>', {
class: 'items-panel empty',
id: 'level_' + levels + '_panel',
text: config.defaultDes
});
distPanel.appendTo(liElement);
liElement.appendTo(distItemContainer);
}
distItemContainer.appendTo(target);
// ======生成DOM结构======
// 获取初始化数据
el.trigger('ca.afterInit', [config, config.levels - 1]);
// 初始化完成,提供回调执行
el.trigger('ca.onCreated');
};
module.exports = function(options) {
return new CascadingAddress(options);
};
... ...
.cascading-address {
position: relative;
.dist-item {
position: relative;
display: inline-block;
padding: 8px;
border: 1px solid #eee;
margin-right: 8px;
font-size: 12px;
color: #1b1b1b;
text-align: center;
&:hover {
cursor: pointer;
}
}
.open {
border: 1px solid #1b1b1b;
border-bottom-width: 0;
z-index: 1000;
background: #fff;
}
.iconfont {
font-size: 14px;
}
.items-indicator {
li {
float: left;
}
}
.items-panel {
background: #fff;
padding: 7px 15px;
border: 1px solid #1b1b1b;
position: absolute;
top: 36px;
z-index: 1;
display: none;
width: 310px;
li {
float: left;
margin: 0 0 14px;
width: 69px;
position: relative;
.check-icon {
display: none;
float: left;
width: 12px;
margin-left: -18px;
padding: 3px;
}
}
li:first-child {
margin-left: 0;
}
li:last-child {
margin-right: 0;
}
a {
font-size: 12px;
display: inline-block;
width: 50px;
padding: 3px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
text-align: left;
}
.checked {
background: #3a3a3a;
color: #fff;
}
}
}
... ...
... ... @@ -2,3 +2,4 @@
@import "goods"; /* 商品 */
@import "floor"; /* 楼层 */
@import "path-nav"; /* 面包屑 */
@import "cascading-address"; /* 区域/地址组件 */
... ...