修复若干bug
This commit is contained in:
@@ -56,6 +56,36 @@ class Framework extends Component{
|
||||
return sels;
|
||||
}
|
||||
|
||||
upDate(sels, enable){
|
||||
let { dataObj } = this.state;
|
||||
let { prop, tree, cascader } = this.props;
|
||||
let { value, disabled, children } = prop;
|
||||
sels.map(sel => dataObj[typeof sel === 'object' ? sel[value] : sel]).filter(a => a).forEach(item => {
|
||||
item[disabled] = !enable
|
||||
|
||||
//严格模式下操作
|
||||
if(tree.strict || cascader.strict){
|
||||
if(enable){//启用父节点, 向上父节点启用
|
||||
let parent = item
|
||||
while(parent){
|
||||
parent[disabled] = false;
|
||||
parent = parent.__node.parent
|
||||
}
|
||||
}
|
||||
//启用禁用节点 子节点启用禁用
|
||||
const upChild = (parent) => {
|
||||
parent[disabled] = !enable
|
||||
let child = parent[children];
|
||||
if(child && isArray(child)){
|
||||
child.forEach(c => upChild(c))
|
||||
}
|
||||
}
|
||||
upChild(item);
|
||||
}
|
||||
})
|
||||
this.setState({ dataObj })
|
||||
}
|
||||
|
||||
exchangeValue(arr, dataObj = this.state.dataObj){
|
||||
let list = arr.map(sel => typeof sel === 'object' ? { ...sel, __node: {} } : dataObj[sel]).filter(a => a)
|
||||
let filterGroup = true, { tree } = this.props;
|
||||
@@ -71,14 +101,19 @@ class Framework extends Component{
|
||||
show = this.state.show;
|
||||
}
|
||||
|
||||
const { prop, tree } = this.props;
|
||||
const { prop, tree, cascader } = this.props;
|
||||
let changeData = this.exchangeValue(sels);
|
||||
if(tree.show && tree.strict){
|
||||
|
||||
//检测是否超选了
|
||||
if(this.checkMax(changeData, changeData)){
|
||||
return ;
|
||||
}
|
||||
|
||||
if(tree.show && tree.strict || cascader.show && cascader.strict){
|
||||
let data = this.state.data;
|
||||
this.clearAndReset(data, changeData);
|
||||
changeData = this.init({ data, prop }, true);
|
||||
}
|
||||
|
||||
this.resetSelectValue(changeData, changeData, true, listenOn);
|
||||
this.setState({ show })
|
||||
}
|
||||
@@ -148,7 +183,7 @@ class Framework extends Component{
|
||||
this.setState({ tmpColor });
|
||||
}
|
||||
|
||||
treeHandler(sels, parent, change, type){
|
||||
treeHandler(sels, parent, change, type, changeStatus){
|
||||
const { value, selected, disabled, children, optgroup } = this.props.prop;
|
||||
let child = parent[children];
|
||||
child.filter(item => !(item[disabled] || item.__node.disabled)).forEach(item => {
|
||||
@@ -169,37 +204,61 @@ class Framework extends Component{
|
||||
}
|
||||
}
|
||||
})
|
||||
let len = child.length;
|
||||
let slen = child.filter(i => sels.findIndex(sel => sel[value] === i[value]) !== -1 || i.__node.selected === true).length;
|
||||
parent.__node.selected = slen === len;
|
||||
parent.__node.half = slen > 0 && slen < len;
|
||||
if(changeStatus){
|
||||
let len = child.length;
|
||||
let slen = child.filter(i => sels.findIndex(sel => sel[value] === i[value]) !== -1 || i.__node.selected === true).length;
|
||||
parent.__node.selected = slen === len;
|
||||
parent.__node.half = slen > 0 && slen < len;
|
||||
}
|
||||
}
|
||||
|
||||
checkMax(item, sels){
|
||||
const { max, maxMethod, theme } = this.props
|
||||
//查看是否设置了多选上限
|
||||
let maxCount = toNum(max);
|
||||
if(maxCount > 0 && sels.length >= maxCount){
|
||||
this.updateBorderColor(theme.maxColor);
|
||||
//查看是否需要回调
|
||||
maxMethod && isFunction(maxMethod) && maxMethod(sels, item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//选项, 选中状态, 禁用状态, 是否强制删除:在label上点击删除
|
||||
itemClick(item, itemSelected, itemDisabled, mandatoryDelete){
|
||||
|
||||
const { theme, prop, radio, repeat, clickClose, max, maxMethod, tree } = this.props
|
||||
let { sels } = this.state
|
||||
let sels = [ ...this.state.sels ]
|
||||
const { value, selected, disabled, children, optgroup } = prop
|
||||
|
||||
//如果是禁用状态, 不能进行操作
|
||||
if(itemDisabled) return;
|
||||
|
||||
if(item[optgroup] && tree.strict){
|
||||
let child = item[children], change = [], isAdd = true;
|
||||
let child = item[children], change = [], isAdd = true, handlerType;
|
||||
if(item.__node.selected){
|
||||
this.treeHandler(sels, item, change, 'del');
|
||||
handlerType = 'del';
|
||||
isAdd = false;
|
||||
}else if(item.__node.half){
|
||||
this.treeHandler(sels, item, change, 'half');
|
||||
handlerType = 'half';
|
||||
this.treeHandler(sels, item, change, handlerType);
|
||||
//无法操作禁用状态, 变成取消操作
|
||||
if(change.length === 0){
|
||||
this.treeHandler(sels, item, change, 'del');
|
||||
handlerType = 'del';
|
||||
isAdd = false;
|
||||
}
|
||||
}else{
|
||||
this.treeHandler(sels, item, change, 'add');
|
||||
handlerType = 'add';
|
||||
}
|
||||
if(handlerType != 'half'){
|
||||
this.treeHandler(sels, item, change, handlerType);
|
||||
}
|
||||
if(this.checkMax(change, change)){
|
||||
return ;
|
||||
}
|
||||
sels = [ ...this.state.sels ], change = [];
|
||||
this.treeHandler(sels, item, change, handlerType, true);
|
||||
|
||||
this.resetSelectValue(sels, change, isAdd);
|
||||
this.setState({ data: this.state.data })
|
||||
}else{
|
||||
@@ -212,11 +271,7 @@ class Framework extends Component{
|
||||
}
|
||||
}else{
|
||||
//查看是否设置了多选上限
|
||||
let maxCount = toNum(max);
|
||||
if(maxCount > 0 && sels.length >= maxCount){
|
||||
this.updateBorderColor(theme.maxColor);
|
||||
//查看是否需要回调
|
||||
maxMethod && isFunction(maxMethod) && maxMethod(sels, item);
|
||||
if(this.checkMax(item, sels)){
|
||||
return ;
|
||||
}
|
||||
|
||||
@@ -395,7 +450,13 @@ class Framework extends Component{
|
||||
|
||||
return (
|
||||
<xm-select { ...xmSelectProps } >
|
||||
<input class="xm-select-default" lay-verify={ config.layVerify } lay-verType={ config.layVerType } name={ config.name } value={ sels.map(item => item[prop.value]).join(',') }></input>
|
||||
<input class="xm-select-default"
|
||||
lay-verify={ config.layVerify }
|
||||
lay-verType={ config.layVerType }
|
||||
lay-reqText={ config.layReqText }
|
||||
name={ config.name }
|
||||
value={ sels.map(item => item[prop.value]).join(',') }
|
||||
></input>
|
||||
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
|
||||
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
|
||||
<Label { ...labelProps } ref={ ref => this.labelView = ref } />
|
||||
|
||||
@@ -81,18 +81,28 @@ class Cascader extends Component{
|
||||
borderColor: theme.color,
|
||||
};
|
||||
|
||||
const isParent = item[children] && item[children].length > 0;
|
||||
const itemStyle = { backgroundColor: 'transparent' }
|
||||
const className = ['xm-option', (dis ? ' disabled' : ''), (selected ? ' selected' : ''), (showIcon ? 'show-icon' : 'hide-icon') ].join(' ');
|
||||
const iconClass = ['xm-option-icon', (() => {
|
||||
//如果是半选状态,但是没有配置半选图标就用默认的
|
||||
if(half){
|
||||
return config.iconfont.half ? config.iconfont.half + ' xm-custom-icon' : 0;
|
||||
const iconClass = (() => {
|
||||
if(isParent && config.iconfont.parent === 'hidden'){
|
||||
return 'xm-option-icon-hidden'
|
||||
}
|
||||
if(selected){
|
||||
return config.iconfont.select ? config.iconfont.select : 0;
|
||||
}
|
||||
return config.iconfont.unselect ? config.iconfont.unselect + ' xm-custom-icon' : 0;
|
||||
})() || ('xm-iconfont ' + (radio ? 'xm-icon-danx' : cascader.strict && half ? 'xm-icon-banxuan' : 'xm-icon-duox'))].join(' ');
|
||||
return ['xm-option-icon', (() => {
|
||||
//如果是半选状态,但是没有配置半选图标就用默认的
|
||||
if(half){
|
||||
return config.iconfont.half ? config.iconfont.half + ' xm-custom-icon' : 0;
|
||||
}
|
||||
if(isParent && config.iconfont.parent){
|
||||
return config.iconfont.parent + ' xm-custom-icon';
|
||||
}
|
||||
if(selected){
|
||||
return config.iconfont.select ? config.iconfont.select : 0;
|
||||
}
|
||||
return config.iconfont.unselect ? config.iconfont.unselect + ' xm-custom-icon' : 0;
|
||||
})() || ('xm-iconfont ' + (radio ? 'xm-icon-danx' : cascader.strict && half ? 'xm-icon-banxuan' : 'xm-icon-duox'))].join(' ');
|
||||
})()
|
||||
|
||||
|
||||
if(item[value] === this.state.val){
|
||||
itemStyle.backgroundColor = theme.hover
|
||||
|
||||
@@ -182,6 +182,9 @@ class General extends Component{
|
||||
}
|
||||
let val = data[index][value];
|
||||
this.setState({ val })
|
||||
//键盘选中时滚动到可视范围内
|
||||
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
|
||||
opt && opt.scrollIntoView(false)
|
||||
}else
|
||||
//Down 键
|
||||
if(keyCode === 40){
|
||||
@@ -192,6 +195,9 @@ class General extends Component{
|
||||
}
|
||||
let val = data[index][value];
|
||||
this.setState({ val })
|
||||
//键盘选中时滚动到可视范围内
|
||||
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
|
||||
opt && opt.scrollIntoView(false)
|
||||
}else
|
||||
//Enter 键
|
||||
if(keyCode === 13){
|
||||
|
||||
@@ -137,27 +137,45 @@ class Tree extends Component{
|
||||
}
|
||||
}
|
||||
|
||||
filterData(data, val){
|
||||
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);
|
||||
item.__node.hidn = val ? child.filter(c => !c.__node.hidn).length === 0 : false;
|
||||
if(!item.__node.hidn){
|
||||
//过滤出来子节点的数据
|
||||
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 })
|
||||
}
|
||||
return
|
||||
}
|
||||
if(tree.strict){
|
||||
return
|
||||
}
|
||||
}
|
||||
item.__node.hidn = val ? !filterMethod(val, item, index, prop) : false;
|
||||
item.__node.hidn = hiddenStatus;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -198,10 +216,10 @@ class Tree extends Component{
|
||||
}
|
||||
|
||||
render(config, { expandedKeys }) {
|
||||
let { prop, empty, sels, theme, radio, template, data, tree, filterable, remoteSearch, searchTips } = config;
|
||||
let { prop, empty, sels, theme, radio, template, data, tree, filterable, remoteSearch, searchTips, iconfont } = config;
|
||||
let { name, value, disabled, children, optgroup } = prop;
|
||||
|
||||
const showIcon = config.model.icon != 'hidden';
|
||||
let showIcon = config.model.icon != 'hidden';
|
||||
const renderItem = (item, indent, expand) => {
|
||||
//是否被选中
|
||||
let selected = !!sels.find(sel => sel[value] == item[value]);
|
||||
@@ -236,16 +254,24 @@ class Tree extends Component{
|
||||
dis && (itemStyle.backgroundColor = '#C2C2C2');
|
||||
}
|
||||
const className = ['xm-option', (dis ? ' disabled' : ''), (selected ? ' selected' : ''), (showIcon ? 'show-icon' : 'hide-icon') ].join(' ');
|
||||
const iconClass = ['xm-option-icon', (() => {
|
||||
//如果是半选状态,但是没有配置半选图标就用默认的
|
||||
if(half){
|
||||
return config.iconfont.half ? config.iconfont.half + ' xm-custom-icon' : 0;
|
||||
const iconClass = (() => {
|
||||
if(expand !== 0 && iconfont.parent === 'hidden'){
|
||||
return 'xm-option-icon-hidden'
|
||||
}
|
||||
if(selected){
|
||||
return config.iconfont.select ? config.iconfont.select : 0;
|
||||
}
|
||||
return config.iconfont.unselect ? config.iconfont.unselect + ' xm-custom-icon' : 0;
|
||||
})() || ('xm-iconfont ' + (radio ? 'xm-icon-danx' : tree.strict && half ? 'xm-icon-banxuan' : 'xm-icon-duox'))].join(' ');
|
||||
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(' ');
|
||||
|
||||
@@ -321,7 +347,18 @@ class Tree extends Component{
|
||||
|
||||
//工具条操作
|
||||
function flat(list, array){
|
||||
array.forEach(item => item[optgroup] ? (!tree.strict && list.push(item), flat(list, item[children])) : list.push(item))
|
||||
//array.forEach(item => item[optgroup] ? (!tree.strict && list.push(item), flat(list, item[children])) : list.push(item))
|
||||
array.forEach(item => {
|
||||
if(item[optgroup]){
|
||||
//非严格模式, 如果隐藏父节点, 证明不可选
|
||||
if(!tree.strict && iconfont.parent !== 'hidden'){
|
||||
list.push(item)
|
||||
}
|
||||
flat(list, item[children])
|
||||
}else{
|
||||
list.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
const toolbar = (
|
||||
<div class='xm-toolbar'>
|
||||
@@ -333,7 +370,8 @@ class Tree extends Component{
|
||||
info = { icon: 'xm-iconfont xm-icon-quanxuan', name, method: (pageData) => {
|
||||
let list = [];
|
||||
flat(list, pageData);
|
||||
list = list.filter(item => !item[disabled])
|
||||
//过滤掉禁用状态的不操作, 隐藏状态的不操作
|
||||
list = list.filter(item => !item[disabled] && !item.__node.hidn)
|
||||
this.props.onReset(radio ? list.slice(0, 1) : mergeArr(list, sels, prop), 'treeData');
|
||||
} };
|
||||
}else if(tool === 'CLEAR'){
|
||||
@@ -344,7 +382,7 @@ class Tree extends Component{
|
||||
info = { icon: 'xm-iconfont xm-icon-fanxuan', name, method: (pageData) => {
|
||||
let list = [];
|
||||
flat(list, pageData);
|
||||
list = list.filter(item => !item[disabled])
|
||||
list = list.filter(item => !item[disabled] && !item.__node.hidn)
|
||||
let selectedList = [];
|
||||
sels.forEach(item => {
|
||||
let index = list.findIndex(pageItem => pageItem[value] === item[value]);
|
||||
|
||||
@@ -93,7 +93,7 @@ class xmOptions {
|
||||
list = []
|
||||
toSimple(data, sels, list, prop);
|
||||
}
|
||||
|
||||
|
||||
let arr = delProp(list, prop.children, [ '__node' ]);;
|
||||
|
||||
if(type === 'name'){
|
||||
@@ -160,6 +160,88 @@ class xmOptions {
|
||||
childData[this.options.el].updateBorderColor(showColor)
|
||||
)
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选中的节点
|
||||
* leafOnly: 是否只是叶子节点,默认值为 false
|
||||
* includeHalfChecked: 是否包含半选节点,默认值为 false
|
||||
*/
|
||||
getTreeValue(leafOnly, includeHalfChecked){
|
||||
const { tree, cascader, prop } = this.options;
|
||||
const { value } = prop;
|
||||
|
||||
//如果不是树状结构, 直接使用getValue
|
||||
if(!(tree.show || cascader.show)){
|
||||
return this.getValue(leafOnly);
|
||||
}
|
||||
|
||||
//获得当前已经选中的数据
|
||||
let sels = childData[this.options.el].state.sels;
|
||||
|
||||
//存储选中的数据
|
||||
let list = [];
|
||||
|
||||
let nodeType = tree.nodeType;
|
||||
|
||||
const listPush = (item, type) => {
|
||||
if(!list.find(i => i[value] === item[value])){
|
||||
item = { ...item }
|
||||
item[nodeType] = type;
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 0; i < sels.length; i++){
|
||||
let node = { ...sels[i] };
|
||||
//首页先把子节点放入到数据中
|
||||
listPush(node, 'leaf');
|
||||
while(node = node.__node.parent){
|
||||
let { half, selected } = node.__node
|
||||
//如果想要父节点, 检测父节点是否为选中状态
|
||||
if(!leafOnly && selected){
|
||||
listPush(node, 'parent');
|
||||
}else
|
||||
//如果是需要半选状态, 并且处于半选状态
|
||||
if(includeHalfChecked && half && !selected){
|
||||
listPush(node, 'half');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let arr = delProp(list, prop.children, [ '__node' ]);;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态启用一些选项
|
||||
*/
|
||||
enable(sels){
|
||||
if(!isArray(sels)){
|
||||
warn('请传入数组结构...')
|
||||
return ;
|
||||
}
|
||||
if(sels.length === 0){
|
||||
return ;
|
||||
}
|
||||
childData[this.options.el].upDate(sels, true)
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态禁用一些选项
|
||||
*/
|
||||
disable(sels){
|
||||
if(!isArray(sels)){
|
||||
warn('请传入数组结构...')
|
||||
return ;
|
||||
}
|
||||
if(sels.length === 0){
|
||||
return ;
|
||||
}
|
||||
childData[this.options.el].upDate(sels, false)
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ export default function (lan = 'zn') {
|
||||
//表单验证
|
||||
layVerify: '',
|
||||
//验证类型
|
||||
layVerType: '',
|
||||
layVerType: '',
|
||||
//验证提示
|
||||
layReqText: '',
|
||||
//尺寸
|
||||
size: 'medium',
|
||||
//是否禁用多选
|
||||
@@ -102,7 +104,9 @@ export default function (lan = 'zn') {
|
||||
//懒加载回调
|
||||
load: null,
|
||||
//是否开启极简模式
|
||||
simple: false,
|
||||
simple: false,
|
||||
//标注节点类型的key
|
||||
nodeType: '__node_type',
|
||||
},
|
||||
//级联结构
|
||||
cascader: {
|
||||
@@ -157,6 +161,7 @@ export default function (lan = 'zn') {
|
||||
select: '',
|
||||
unselect: '',
|
||||
half: '',
|
||||
parent: '',
|
||||
},
|
||||
// 展开下拉框
|
||||
show(){
|
||||
|
||||
@@ -243,6 +243,9 @@ xm-select{
|
||||
color: unset;
|
||||
border: unset;
|
||||
}
|
||||
&-hidden{
|
||||
margin-right: -10px;
|
||||
}
|
||||
}
|
||||
&-icon.xm-icon-danx{
|
||||
border-radius: 100%;
|
||||
|
||||
Reference in New Issue
Block a user