-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchain-select.js
224 lines (184 loc) · 5.85 KB
/
chain-select.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
!(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], function($) {
return factory($);
});
} else if (typeof exports === 'object' && module.exports) {
module.exports = factory;
} else {
return root['ChainSelect'] = factory(root.jQuery);
}
}(this, function($) {
var ChainSelect = function(options) {
var opts = $.extend({}, {
prompt: '-请选择-',
data: null,
// 字段名映射
alias: {
value: 'value', // select option 的 value 值字段名
label: 'label', // select option 的文本字段名
children: 'children', // 子级字段名
name: 'name' // 表单项 name 字段名
}
}, options);
this.$el = opts.el instanceof jQuery ? opts.el : $(opts.el);
this.$parent = this.$el.parent();
this.className = this.$el.prop('class') || '';
this.prompt = opts.prompt;
this.alias = opts.alias;
this.cache = null;
if (this.$el.length && opts.data) {
this.data = [].concat(opts.data);
setItemsLevel(this.data, this.alias.children, 0);
this.render(this.data);
this.bindEvt();
this.setDefaultValues(opts.values);
}
};
ChainSelect.prototype = {
constructor: ChainSelect,
// 设置各个 select 的缺省值
setDefaultValues: function(values) {
var that = this;
var $parent = this.$parent;
if (Array.isArray(values)) {
values.forEach(function(value, idx) {
var curSelect = $parent.children('select').eq(idx);
that.cache = that.getCurListData(value, idx, that.data);
that.showNext(idx + 1);
curSelect.val(value);
});
}
},
// 获取当前 select value 值对应的数据(当前所有 option)
getCurListData: function(value, level, data) {
if (!data) {
return {};
}
var alias = this.alias;
var queue = [].concat(data); // 使用数组模拟队列
// 广度遍历对象
while(queue.length !== 0) {
var children;
data = queue.shift(); // 取出队列第一顶
// 值相等且层级相同即刻返回(不同层级相同 value 的情况)
// 注:'1' == 1
if (getChainObjData(data, alias.value) == value && data._level === level) {
return data;
}
children = getChainObjData(data, alias.children);
// 将子级加入遍历队列
if (children) {
queue = queue.concat(children);
}
}
},
// 绑定事件
bindEvt: function() {
var that = this;
this.$el.parent()
.on('change', 'select', function() {
var value = $(this).val();
var level = Number($(this).data('level'));
that.cache = that.getCurListData(value, level, that.data);
// 移除后续所有 select
$(this).nextAll('select').remove();
if (value !== that.prompt) {
that.showNext(level + 1);
}
if (typeof that.changeCallback === 'function') {
that.changeCallback(value, that.cache, $(this));
}
});
},
// 显示下一个 select(如有)
showNext: function(level) {
var nextListData = getChainObjData(this.cache, this.alias.children);
if (nextListData && nextListData.length) {
this.$parent.children('select[data-level]:last')
.after(this.render(nextListData, level));
}
},
// 渲染 view
render: function(options, level) {
var html = '';
var name = getChainObjData(options[0], this.alias.name);
if (level) {
html += (
' <select class="' + this.className + '" ' +
'data-level="' + level + '"' +
(name ? ' name="' + name + '"' : '') +
'>'
);
}
html += this.builder(options);
if (!level) {
this.$el
.attr('data-level', 0)
.prop('name', name)
.html(html);
} else {
html += '</select>';
this.$parent.children('select:last').after(html);
}
},
// 构建 select 的 options
builder: function(options) {
var html = '';
var alias = this.alias;
if (this.prompt) {
html += '<option>' + this.prompt + '</option>';
}
html += (
options.map(function(option) {
var value = getChainObjData(option, alias.value) || '';
var label = getChainObjData(option, alias.label);
// 如果 label 不存在,则使用 value 代替
if (typeof label === 'undefined') {
label = value;
}
return '<option value="' + value + '">' + label + '</option>';
}).join('')
);
return html;
},
onChanged: function(fn) {
this.changeCallback = fn;
},
setValues: function(values) {
this.$el.nextAll('select').remove();
this.setDefaultValues(values);
}
}
// 递归数组,为每项设置 level 值
function setItemsLevel(arr, children_ns, level) {
arr.forEach(function(item) {
item._level = level;
if (Array.isArray(item[children_ns])) {
setItemsLevel(item[children_ns], children_ns, level + 1);
}
});
}
/**
* 获取链式对象的字符串对应的值
* @param {object | array} data - 原对象或数组
* @param {string | number} target - 目标对象字符串
* @example:
getChainObjData({a: 1, b: {c: 3}}, 'b.c') -> 3
*/
function getChainObjData(data, target) {
if (target == undefined) { // undefined | null
return data;
}
var fields = String(target).split('.');
// 累加链式对象的每一项
return fields.reduce(function(obj, field) {
if (obj) {
return obj[field];
} else {
return null; // 任何一项不存在则最终返回 null
}
}, data);
};
return ChainSelect;
}));