LaJiFenLei/Waste.Web.Entry/wwwroot/js/ajaxCascader/ajaxCascader.src.js

699 lines
19 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @Name: 基于layui的异步无限级联选择器
* @Author: 前端喵
* 创建时间: 2019/05/23
* 使用说明: 在主文件里面使用layui.config设置具体方法看index.html
*/
/**
* 参数说明:
* width可选 :input框宽度
* height可选input框高度
*/
layui.define(["jquery"],(exports)=>{
let $ = layui.jquery;
// 私有方法,禁止外面调用的方法
function Private(){
//页面初始化默认值
this.param = {
width: 220,
height: 40,
prop: {
value: "value",
label: "label",
children: 'children'
},
clicklast: false,
time: 250,
placeholder: "请选择",
search: {
show: false,
minLabel: 10,
placeholder: '请输入搜索词'
}
},
// 定义全局状态仓库
this.store = {
showCascader: false,
cascaderDom: null, // 当前elem对象
cascaderAll: null, // 生成的Dom主对象
input: null, // input框dom
inputI: null, // input框箭头dom
model: null, // 下拉菜单的主dom
li: null, // li标签
parentNextAll: null, // 当前操作的li标签元素后面所有ul集合
brother: null, // li标签同级dom集合
data: [], // 所有从后端异步请求的数据集合
chooseData: [], // 已选中的数据集
zIndex: 2000 // 显示顺序
}
}
// 页面初始化
Private.prototype.init = function(options){
let store = this.store
let param = this.param
// dom变量初始化
store.cascaderDom = $(options.elem)
// 把用户的参数值进行存储
// 开始存储
for (let i in options) {
if (options[i].length !== 0) {
if (i == "prop" | i == 'search') {
for (let x in options[i]) {
param[i][x] = options[i][x]
}
} else {
param[i] = options[i]
}
}
}
delete param.data
if (options.data) {
store.data = options.data
}
param.device = this.checkDevice()
// 存储结束
if (store.cascaderDom.next().hasClass('cascader-all')) {
store.cascaderDom.next().remove()
}
param.className = 'cascader-' + this.param.elem.replace('#', '')
let phoneName = ''
if (param.device === 1) {
phoneName = 'cascader-model-phone'
}
// 渲染主dom
store.cascaderDom.after(`
<div class="cascader-all ` + param.className + `" style="width:`+this.param.width+`px;">
<input type="text" class="layui-input cascader-input" placeholder="`+param.placeholder+`" readonly style="width:`+this.param.width+`px;height:`+this.param.height+`px;">
<i class="layui-icon layui-icon-down cascader-i" style="top:`+this.param.height/2+`px;"></i>
<div class="cascader-model ` + phoneName + `" style="z-index:`+this.store.zIndex+`;display:flex;">
</div>
</div>
`)
// 判断elem是否存在以及是否正确elem必填
if(!options.elem || options.elem == ""){
layer.msg('请配置有效的elem值 ')
}else if($(options.elem).length == 0){
layer.msg('请配置有效的elem值 ')
}
store.input = store.cascaderDom.nextAll().find('.cascader-input')
store.inputI = store.input.next()
store.cascaderAll = $(store.cascaderDom.nextAll()[0])
store.model = store.cascaderDom.nextAll().find('.cascader-model')
store.li = store.model.find('li')
// 全局状态初始化
store.model.hide()
if (store.data.length == 0) {
param.getChildren(param.value, data => {
store.data = data
this.liHtml(store.data)
if (param.checkData) {
if (param.checkData.length > 0) {
this.dataToshow(param.checkData)
}
}
})
} else {
this.liHtml(store.data)
if (param.checkData) {
if (param.checkData.length > 0) {
this.dataToshow(param.checkData)
}
}
}
// 先进入是否禁用判断事件
// 不禁用则执行下面的事件
this.disabled()
.then(res => {
this.inputClick(options)
this.liClick()
this.liHover()
this.modelHandle()
if (param.search.show) {
this.handleSearch()
}
})
}
// 判断是否禁用
Private.prototype.disabled = function() {
const disabled = this.param.disabled
const cascaderAll = this.store.cascaderAll
const input = this.store.input
return new Promise((resolve, reject) => {
if (disabled) {
cascaderAll.addClass('cascader-disabled')
}else{
resolve()
}
})
}
// li标签赋值方法
// key为string类型
Private.prototype.liHtml = function(data, key, choose){
let lis=[]
let param = this.param
let store = this.store
let position = []
let key1 = ''
if (!key || key.length == 0) {
key=""
} else {
key = key.join('-')
key1 = key
key = key+"-"
}
if (data !== "") {
for (let i in data) {
let li = '<li value="'+data[i][param.prop.value]+'" key="'+key+i+'"'
if (i == choose) {
li = li +' class="cascader-choose-active"'
position = [i,data.length]
}
// 来自于陈诗樵的BUG修复,github地址:https://github.com/alasq
// data[i].children 改为 data[i][param.prop.children]
if (data[i].hasChild == true || data[i][param.prop.children]) {
li = li+'>'+data[i][param.prop.label]+'<i class="layui-icon layui-icon-right"></i></li>'
} else {
li = li+'>'+data[i][param.prop.label]+'</li>'
}
lis.push(li)
}
}
lis = lis.join('')
if (data && data.length>0){
if (param.search.show && data.length > param.search.minLabel) {
lis = '<input class="layui-input cascader-model-input" key="'+ key1 + '" placeholder="'+ param.search.placeholder +'">' + lis
}
} else {
lis = '<p class="nodata">暂无数据</p>'
console.log('数据为空,无法进行渲染')
}
let ul = $(`
<ul class="cascader-ul">`+lis+`</ul>
`)
ul.fadeIn('fast')
store.model.append(ul)
this.liPosition(position)
this.ModelPosition()
}
// 当前选中的跳转位置
Private.prototype.liPosition = function(position) {
let model = this.store.model.find('ul').last()
}
Private.prototype.handleSearch = function() {
let model = this.store.model
let prop = this.param.prop
let _this = this
let value = ''
let flag = true
model.on('compositionstart',function(){
flag = false
})
model.on('compositionend',function(){
flag = true
})
model.on('input', 'input', function() {
setTimeout(()=>{
if (flag) {
let data = _this.store.data
if (value == this.value) {
return
}
value = this.value
let key = $(this).attr('key').split('-')
let key1 = $(this).attr('key') + '-'
if ($(this).attr('key')) {
for (i in key) {
if (data[key[i]][prop.children]){
data = data[key[i]][prop.children]
}
}
}
let renderData = []
let lis = ''
for (i in data) {
if (data[i][prop.label].indexOf(value) > -1) {
if (data[i][prop.children] | data[i].hasChild) {
lis += '<li value="'+data[i][prop.value]+'" key="'+key1+i+'">'+data[i][prop.label]+'<i class="layui-icon layui-icon-right"></i></li>'
} else {
lis += '<li value="'+data[i][prop.value]+'" key="'+key1+i+'">'+data[i][prop.label]+'</li>'
}
renderData.push(data[i])
}
}
$(this.parentNode).find('li').remove()
$(this.parentNode).append(lis)
}
}, 0)
})
}
Private.prototype.modelHandle = function() {
$(window).resize(() => { //当浏览器大小变化时
let model = this.store.model
this.ModelPosition()
})
}
let modelWidth = 0
Private.prototype.ModelPosition = function() {
let model = this.store.model
let input = this.store.input
let BodyWidth = document.documentElement.clientWidth
let positionLeft = 0,
left = 0
if (window.getComputedStyle(model[0]).width !== "auto") {
modelWidth = window.getComputedStyle(model[0]).width.replace('px','')
}
left = input.offset().left - model.position().left
if (BodyWidth < modelWidth) {
positionLeft = BodyWidth - modelWidth
}
if (positionLeft < 0) {
model.css("left",positionLeft - 30)
} else {
model.css('left',0)
}
}
// 鼠标hover监听事件[li标签]
Private.prototype.liHover = function(){
let store = this.store
let param = this.param
let _this = this
store.model.on('mouseenter', 'li', function() {
store.parentNextAll = $(this).parent("ul").nextAll()
store.brother = $(this).siblings()
if ($(this).find('i').length == 0) {
store.parentNextAll.fadeOut('fast', function() {
store.parentNextAll.remove()
})
} else {
let keys=$(this).attr('key')
let value = $(this).attr('value')
let data = _this.store.data
let childrenName = _this.param.prop.children
keys = keys.split('-')
let goodData = data
for (let i in keys) {
let key = keys[i]
if (goodData) {
if (goodData[key]) {
goodData = goodData[key][childrenName]
}
}
}
if (!goodData) {
param.getChildren(value, datax => {
goodData = datax
let children = data
if (goodData && goodData.length != 0) {
for (let i in keys) {
if (i == keys.length - 1) {
children = goodData
} else {
if (!children[keys[i]][childrenName]) {
children[keys[i]][childrenName] = new Array()
}
children = children[keys[i]][childrenName]
}
}
DataTreeAdd(data,goodData,keys)
store.parentNextAll = $(this).parent("ul").nextAll()
store.parentNextAll.remove()
_this.liHtml(goodData, keys)
} else {
$(this).find('i').remove()
store.parentNextAll.remove()
DataTreeChange(data, keys)
}
})
} else {
store.parentNextAll.remove()
_this.liHtml(goodData, keys)
}
// 增加data的树结点
function DataTreeAdd(data, newData, keys) {
let array = data
for (let k in keys) {
if (k < keys.length -1) {
array = array[keys[k]][param.prop.children]
} else {
array = array[keys[k]]
}
}
array.children = newData
}
function DataTreeChange(data, keys) {
let array = data
for (let k in keys) {
if (k < keys.length -1) {
array = array[keys[k]][param.prop.children]
} else {
array = array[keys[k]]
}
}
array.hasChild = false
}
}
$(this).addClass('cascader-choose-active')
store.brother.removeClass('cascader-choose-active')
store.parentNextAll.children().removeClass('cascader-choose-active')
// 获取所有的已选中的数据并回显至input选择框中
// _this.getChooseData();
});
}
// 鼠标点击监听事件[li标签]
Private.prototype.liClick = function() {
let _this = this
let store = this.store
let param = this.param
let className = param.className
// store.model为一个自定义dom对象
if (param.clicklast == false) {
store.model.on('click', 'li', function() {
_this.getChooseData()
store.showCascader = !store.showCascader
if (param.device === 1){
} else {
store.model.slideUp(_this.param.time)
}
store.inputI.removeClass('rotate')
})
} else {
store.model.on('click', 'li', function() {
store.parentNextAll = $(this).parent("ul").nextAll()
if (store.parentNextAll.length == 0) {
_this.getChooseData()
store.showCascader = !store.showCascader
if (param.device === 1){
} else {
store.model.slideUp(_this.param.time)
}
store.inputI.removeClass('rotate')
_this.getThisData()
}
})
}
}
// 判断当前访问客户端是PC还是移动端
// PC端返回1移动端返回0
Private.prototype.checkDevice = function() {
if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
return 1
} else {
return 0
}
}
// 获取当前层级数据
Private.prototype.getThisData = function() {
let value = this.param.prop.value
,children = this.param.prop.children
,chooseData = this.store.chooseData
,data = this.store.data
,currentData = []
for (const i in chooseData) {
for (const x in data) {
if (chooseData[i] == data[x][value]) {
if (Number(i) === chooseData.length - 1) {
currentData = JSON.parse(JSON.stringify(data[x]))
break
}
if (data[x][children]) {
data = data[x][children]
} else {
data = data[x]
}
break
}
}
}
return currentData
}
// 鼠标监听事件[input控件]
Private.prototype.inputClick = function(options) {
let store = this.store
let param = this.param
let _this = this
$(document).click(function(e) {
let className = e.target.className
className = className.split(" ")
let other = ['cascader-input', 'cascader-model', "cascader-choose-active", "layui-icon-right", "cascader-ul", "cascader-model-input"]
for (let i in className) {
for (let x in other) {
if (className[i] == other[x]) {
return
}
}
}
store.showCascader = false
store.model.slideUp(_this.param.time)
store.inputI.removeClass('rotate')
});
store.input.click(function() {
store.showCascader = !store.showCascader
if (store.showCascader == true) {
store.inputI.addClass('rotate')
let chooseData = _this.store.chooseData
let data = _this.store.data
if (chooseData.length !== 0) {
let key = []
_this.clearModel()
for (let i in chooseData) {
for (let x in data) {
if (data[x][param.prop.value] == chooseData[i]) {
_this.liHtml(data,key,x)
key.unshift(x)
data = data[x][param.prop.children]
break
}
}
}
}
store.model.slideDown(_this.param.time)
_this.ModelPosition()
} else {
store.model.slideUp(_this.param.time)
store.inputI.removeClass('rotate')
}
})
}
// 获取页面中选中的数据
Private.prototype.getChooseData = function() {
let store = this.store
let chooseDom = store.model.find('li.cascader-choose-active')
let chooseData = [];
let chooseLabel = [];
for(let i in chooseDom){
if(chooseDom[i].innerText){
chooseData.push($(chooseDom[i]).attr('value'));
chooseLabel.push(chooseDom[i].innerText);
}else{
break;
}
}
this.store.chooseData = chooseData;
this.inputValueChange(chooseLabel);
}
Private.prototype.inputValueChange = function(label){
let store = this.store;
let param = this.param;
if(param.showlast == true){
label = label[label.length-1];
}else{
label = label.join('/');
}
store.input.val(label);
let fontWidth = store.input.css('font-size').replace('px',''),
inputWidth = store.input.width(),
labelWidth = label.length;
let maxLabelWidth = Math.floor(inputWidth/fontWidth);
if(labelWidth>maxLabelWidth){
store.input.attr('title',label);
}else{
store.input.attr('title',"");
}
}
// 数据回显
Private.prototype.dataToshow = function(checkData){
let param = this.param;
let store = this.store;
let backData = []; //后端数据集合
let chooseLabel=[]; //选中的项对应的label值
let keys=[]; //checkData在数据源中的位置大全
backData[0] = store.data;
let flag = 1;
if(param.getChildren){
if (checkData.length === 1) {
for (let i in store.data) {
if (store.data[i][param.prop.value] == checkData[0]) {
let label = store.data[i][param.prop.label].split(',')
this.inputValueChange(label)
return
}
}
}
for(let i=1; i<checkData.length; i++){
if(i < checkData.length){
param.getChildren(checkData[i-1],data=>{
backData[i] = data;
flag++;
if(flag == checkData.length){
for(let i=checkData.length -1;i>=0;i--){
for(let x in backData[i]){
if(checkData[i] == backData[i][x][param.prop.value]){
keys.unshift(x);
chooseLabel.unshift(backData[i][x][param.prop.label])
if(i < checkData.length -1){
backData[i][x][param.prop.children] = backData[i+1];
}
}
}
}
store.data = backData[0];
// input框数据回显
this.inputValueChange(chooseLabel);
this.clearModel();
// 选择器数据回显
let key = [];
for(let i in backData){
if(i !== "0"){
key.push(keys[i-1]);
}
this.liHtml(backData[i],key,keys[i]);
}
}
});
}
}
}else{
let storeData = store.data;
for(let x in checkData){
for(let i in storeData){
if(storeData[i][param.prop.value] == checkData[x]){
chooseLabel.push(storeData[i][param.prop.label]);
keys.push(i)
storeData = storeData[i][param.prop.children];
break;
}
}
}
// input框数据回显
this.inputValueChange(chooseLabel);
this.store.chooseData = checkData;
}
}
// 清空ul标签
Private.prototype.clearModel = function(){
let store = this.store;
store.model.html('');
}
// 监听下拉菜单的位置
Private.prototype.handlePosition = function(){
// 当前屏幕大小
// let bodyWidth =
}
let privates = new Array();
let dom_num = 0;
// 暴露给外界使用的方法
let cascader = {
// 页面初始化
load: function load(options) {
let current = null
for (let i in privates) {
if(privates[i].elem === options.elem){
current = i
}
}
if (!current) {
current = dom_num
dom_num ++
privates[current] = new Array()
privates[current].obj = new Private()
}
privates[current].elem = options.elem
privates[current].obj.store.zIndex -= current
privates[current].obj.store.data = []
if (options.chooseData) {
privates[current].obj.store.chooseData = options.chooseData
} else {
privates[current].obj.store.chooseData = []
}
privates[current].obj.init(options)
},
// 获取页面中选中的数据,数组形式
getChooseData: function(elem){
let obj = this.elemCheck(elem);
return obj.store.chooseData;
},
// 监听方法
on: function(type,elem,callback){
let obj = this.elemCheck(elem)
let className = obj.param.className
if(type == "click"){
$(document).on('click','.' + className + ' li',function(){
setTimeout(function(){
let data = obj.getThisData()
if(obj.param.clicklast === false){
callback(data)
}else{
if(obj.store.parentNextAll.length == 0){
callback(data)
}
}
},50)
});
}else if(type == "hover"){
obj.store.model.on('mouseenter','li',function(){
callback();
});
}
},
// elem位置判断禁止外界调用因为你调也没啥卵用
elemCheck:function(elem){
if(!elem){
return privates[0].obj;
}
for(let i in privates){
if(privates[i].elem == elem){
return privates[i].obj;
}
}
}
}
layui.link(layui.cache.base + 'ajaxCascader.css');
exports('ajaxCascader',cascader);
});