import { h, Component, render } from 'preact' import { deepMerge, isFunction, mergeArr } from '@/common/util' const emptyVal = {}; class Tree extends Component{ constructor(options){ super(options); this.state = { expandedKeys: [], filterValue: '', remote: true, loading: false, val: emptyVal, } this.searchCid = 0; this.inputOver = true; this.__value = ''; this.tempData = []; } init(props){ const { tree, dataObj, flatData, prop } = props; const { value, optgroup } = prop let keys = []; if(tree.expandedKeys === true){ keys = flatData.filter(item => item[optgroup] === true).map(item => item[value]) }else if(tree.expandedKeys === false){ }else{ tree.expandedKeys.forEach(key => { keys.push(key); let item = dataObj[key]; while(item){ let pkey = item[value]; keys.findIndex(k => k === pkey) === -1 && (keys.push(pkey)) item = item.__node.parent } }); } this.setState({ expandedKeys: keys }) } blockClick(e){ e.stopPropagation(); } optionClick(item, selected, disabled, type, e){ if(type === 'line'){ //加载中的不需要进行处理 if(item.__node.loading === true){ return; } const { tree, prop, sels } = this.props; const { clickExpand, clickCheck } = tree; //检测点击的是不是三角箭头 let isExpand = e.target && isFunction(e.target.getAttribute) && e.target.getAttribute('type') === 'expand' //如果点击即展开 if(clickExpand || isExpand){ //不是父节点的不需要处理 if(!tree.lazy && !item[prop.optgroup]){ this.props.ck(item, selected, disabled); return } let val = item[this.props.prop.value]; let expandedKeys = this.state.expandedKeys; let index = expandedKeys.findIndex(v => v === val); index === -1 ? expandedKeys.push(val) : expandedKeys.splice(index, 1); this.setState({ expandedKeys }); //是否需要懒加载 let child = item[prop.children]; if(tree.lazy && child && child.length === 0 && item.__node.loading !== false){ item.__node.loading = true; tree.load(item, (result) => { item.__node.loading = false; item[prop.children] = this.handlerData(result, prop.children); item[prop.selected] = sels.findIndex(i => i[prop.value] === item[prop.value]) != -1 this.props.onReset(sels, 'treeData'); }); } }else{ if(clickCheck){ type = 'checkbox' } } } if(type === 'checkbox'){ this.props.ck(item, selected, disabled); } //阻止父组件上的事件冒泡 this.blockClick(e); } handlerData(data, children){ return data.map(item => { item.__node = {}; if(item[children]){ item[children] = this.handlerData(item[children], children); } return item; }) } searchInput(e){ let v = e.target.value; if(v === this.__value){ return ; } clearTimeout(this.searchCid); if(this.inputOver){ //保证输入框内的值是实时的 this.__value = v; //让搜索变成异步的 this.searchCid = setTimeout(() => { this.callback = true; this.setState({ filterValue: this.__value, remote: true }) }, this.props.delay); } } focus(){ this.searchInputRef && this.searchInputRef.focus(); } blur(){ this.searchInputRef && this.searchInputRef.blur(); } handleComposition(e){ let type = e.type; if(type === 'compositionstart'){ this.inputOver = false; clearTimeout(this.searchCid); }else if(type === 'compositionend'){ this.inputOver = true; this.searchInput(e); } } filterData(data, val, parentHidden){ const { prop, filterMethod, tree } = this.props; const { children, optgroup, name, value } = prop; data.forEach((item, index) => { //首先判断父节点的状态是显示还是隐藏 let hiddenStatus = val ? !filterMethod(val, item, index, prop) : false; //严格模式下, 不计算父节点的状态 let thisParentHidden; if(tree.strict){ thisParentHidden = false; }else{//非严格模式下, 父节点显示, 子节点无条件显示 thisParentHidden = parentHidden === false ? false : hiddenStatus; hiddenStatus = thisParentHidden } //如果包含了子节点 if(item[optgroup]){ //过滤出来子节点的数据 let child = this.filterData(item[children], val, thisParentHidden); let childHiddenStatus = val ? child.filter(c => !c.__node.hidn).length === 0 : false; //严格模式下子节点都隐藏了, 父节点也不显示 if(tree.strict){ hiddenStatus = childHiddenStatus; }else{//非严格模式, 父节点没有搜索到, 看看子节点有没有显示的 hiddenStatus = thisParentHidden && childHiddenStatus; } if(!hiddenStatus){//如果是显示状态 let keys = this.state.expandedKeys; if(val && keys.findIndex(key => key === item[value]) === -1){ keys.push(item[value]); this.setState({ expandedKeys: keys }) } } } item.__node.hidn = hiddenStatus; }); return data; } postData(){ if(this.state.remote){ this.callback = false; this.setState({ loading: true, remote: false }); //让输入框失去焦点 this.blur(); this.props.remoteMethod(this.state.filterValue, (result, totalSize) => { //回调后可以重新聚焦 this.focus(); this.callback = true; this.setState({ loading: false, totalSize }); this.props.onReset(result, 'data'); }, this.props.show, 1); } } //组件将要接收新属性 componentWillReceiveProps(props){ if(this.props.show != props.show){ if(!props.show){ //清空输入框的值 this.setState({ filterValue: '', val: emptyVal }); this.__value = ''; this.searchInputRef && (this.searchInputRef.value = ''); }else{ //聚焦输入框 setTimeout(() => this.focus(), 0); } } } //组件将要被挂载 componentWillMount(){ this.init(this.props); } render(config, { expandedKeys }) { let { prop, empty, sels, theme, radio, template, data, tree, filterable, remoteSearch, searchTips, iconfont, enableKeyboard } = config; let { name, value, disabled, children, optgroup } = prop; let showIcon = config.model.icon != 'hidden'; const renderItem = (item, indent, expand) => { //是否被选中 let selected = !!sels.find(sel => sel[value] == item[value]); //是否禁用 let dis = item[disabled] // 是否半选 let half = item.__node.half === true; //tree是否遵义严格父子结构 if(tree.strict){ selected = selected || half || item.__node.selected dis = dis || item.__node.disabled; } const iconStyle = selected ? { color: theme.color, border: 'none' } : { borderColor: theme.color, }; const itemStyle = { paddingLeft: indent + 'px' } //处理键盘的选择背景色 if(enableKeyboard && item[value] === this.state.val){ itemStyle.backgroundColor = theme.hover } //隐藏图标的处理 if(!showIcon && selected){ itemStyle.backgroundColor = theme.color; dis && (itemStyle.backgroundColor = '#C2C2C2'); } const className = ['xm-option', (dis ? ' disabled' : ''), (selected ? ' selected' : ''), (showIcon ? 'show-icon' : 'hide-icon') ].join(' '); const iconClass = (() => { if(expand !== 0 && iconfont.parent === 'hidden'){ return 'xm-option-icon-hidden' } return ['xm-option-icon', (() => { //如果是半选状态,但是没有配置半选图标就用默认的 if(half){ return iconfont.half ? iconfont.half + ' xm-custom-icon' : 0; } if(expand !== 0 && iconfont.parent){ return iconfont.parent + ' xm-custom-icon'; } if(selected){ return iconfont.select ? iconfont.select : 0; } return iconfont.unselect ? iconfont.unselect + ' xm-custom-icon' : 0; })() || ('xm-iconfont ' + (radio ? 'xm-icon-danx' : tree.strict && half ? 'xm-icon-banxuan' : 'xm-icon-duox'))].join(' '); })() const treeIconClass = ['xm-tree-icon', expand ? 'expand':'', item[children] && (item[children].length > 0 || (tree.lazy && item.__node.loading !== false)) ? 'xm-visible':'xm-hidden'].join(' '); const iconArray = []; if(tree.showFolderIcon){ iconArray.push(); if(tree.showLine){ if(expand){ iconArray.push() } iconArray.push(); } } //处理鼠标选择的背景色 const hoverChange = e => { if(e.type === 'mouseenter'){ if(!item[disabled]){ if(enableKeyboard){ this.setState({ val: item[value] }) }else{ e.target.style.backgroundColor = theme.hover; } } }else if(e.type === 'mouseleave'){ if(!item[disabled]){ if(enableKeyboard){ }else{ e.target.style.backgroundColor = ''; } } } } return (