Tree 优化

This commit is contained in:
liaoxuezhi 2019-11-10 11:18:22 +08:00
parent 4ac50f3bd7
commit a4d07a5aec
6 changed files with 105 additions and 53 deletions

View File

@ -54,6 +54,15 @@
} }
} }
&-rootItem {
line-height: $Tree-itemHeight;
}
&-item>div:hover>.#{$ns}Tree-item-icons,
&-rootItem>div:hover>.#{$ns}Tree-item-icons {
visibility: visible;
}
&-itemLabel { &-itemLabel {
&:hover { &:hover {
background: $Tree-item-onHover-bg; background: $Tree-item-onHover-bg;
@ -200,7 +209,6 @@
&-itemLabel { &-itemLabel {
user-select: none; user-select: none;
cursor: pointer;
&.is-checked, &.is-checked,
&.is-children-checked { &.is-children-checked {
@ -212,6 +220,10 @@
} }
} }
&-itemText {
cursor: pointer;
}
&-placeholder { &-placeholder {
color: $text--muted-color; color: $text--muted-color;
} }

View File

@ -5,7 +5,14 @@
*/ */
import React from 'react'; import React from 'react';
import {eachTree, isVisible, autobind, findTreeIndex} from '../utils/helper'; import {
eachTree,
isVisible,
autobind,
findTreeIndex,
hasAbility,
createObject
} from '../utils/helper';
import {Option, Options, value2array} from './Checkboxes'; import {Option, Options, value2array} from './Checkboxes';
import {ClassNamesFn, themeable} from '../theme'; import {ClassNamesFn, themeable} from '../theme';
import {highlight} from '../renderers/Form/Options'; import {highlight} from '../renderers/Form/Options';
@ -35,10 +42,10 @@ interface TreeSelectorProps {
// 名称、取值等字段名映射 // 名称、取值等字段名映射
labelField: string; labelField: string;
valueField: string; valueField: string;
iconField?: string; iconField: string;
unfoldedField?: string; unfoldedField: string;
foldedField?: string; foldedField: string;
disabledField?: string; disabledField: string;
className?: string; className?: string;
itemClassName?: string; itemClassName?: string;
@ -59,6 +66,7 @@ interface TreeSelectorProps {
// 是否为内建 增、改、删。当有复杂表单的时候直接抛出去让外层能统一处理 // 是否为内建 增、改、删。当有复杂表单的时候直接抛出去让外层能统一处理
bultinCUD?: boolean; bultinCUD?: boolean;
rootCreatable?: boolean;
creatable?: boolean; creatable?: boolean;
onAdd?: ( onAdd?: (
idx?: number | Array<number>, idx?: number | Array<number>,
@ -444,16 +452,16 @@ export class TreeSelector extends React.Component<
showRadio, showRadio,
multiple, multiple,
disabled, disabled,
labelField: nameField = '', labelField,
valueField = '', valueField,
iconField = '', iconField,
disabledField = '', disabledField,
cascade, cascade,
selfDisabledAffectChildren, selfDisabledAffectChildren,
onlyChildren, onlyChildren,
classnames: cx, classnames: cx,
highlightTxt, highlightTxt,
options: data, options,
maxLength, maxLength,
minLength, minLength,
creatable, creatable,
@ -471,7 +479,7 @@ export class TreeSelector extends React.Component<
let childrenChecked = 0; let childrenChecked = 0;
let ret = list.map((item, key) => { let ret = list.map((item, key) => {
if (!isVisible(item as any, data)) { if (!isVisible(item as any, options)) {
return null; return null;
} }
@ -584,12 +592,13 @@ export class TreeSelector extends React.Component<
) : null} ) : null}
{highlightTxt {highlightTxt
? highlight(item[nameField], highlightTxt) ? highlight(item[labelField], highlightTxt)
: item[nameField]} : item[labelField]}
</span> </span>
{!nodeDisabled && !isAdding && !isEditing ? ( {!nodeDisabled && !isAdding && !isEditing ? (
<div className={cx('Tree-item-icons')}> <div className={cx('Tree-item-icons')}>
{creatable ? ( {creatable && hasAbility(item, 'creatable') ? (
<a <a
onClick={this.handleAdd.bind(this, item)} onClick={this.handleAdd.bind(this, item)}
data-tooltip="添加孩子节点" data-tooltip="添加孩子节点"
@ -597,7 +606,8 @@ export class TreeSelector extends React.Component<
<Icon icon="plus" className="icon" /> <Icon icon="plus" className="icon" />
</a> </a>
) : null} ) : null}
{removable ? (
{removable && hasAbility(item, 'removable') ? (
<a <a
onClick={this.handleRemove.bind(this, item)} onClick={this.handleRemove.bind(this, item)}
data-tooltip="移除该节点" data-tooltip="移除该节点"
@ -605,7 +615,8 @@ export class TreeSelector extends React.Component<
<Icon icon="minus" className="icon" /> <Icon icon="minus" className="icon" />
</a> </a>
) : null} ) : null}
{editable ? (
{editable && hasAbility(item, 'editable') ? (
<a <a
onClick={this.handleEdit.bind(this, item)} onClick={this.handleEdit.bind(this, item)}
data-tooltip="编辑该节点" data-tooltip="编辑该节点"
@ -654,32 +665,32 @@ export class TreeSelector extends React.Component<
rootLabel, rootLabel,
showIcon, showIcon,
classnames: cx, classnames: cx,
creatable creatable,
rootCreatable,
disabled
} = this.props; } = this.props;
let data = this.props.options; let options = this.props.options;
const {value, isAdding, addingParent, isEditing, inputValue} = this.state; const {value, isAdding, addingParent, isEditing, inputValue} = this.state;
let addBtn = null; let addBtn = null;
if (creatable) { if (creatable && hideRoot) {
addBtn = ( addBtn = (
<li> <a
<a className={cx('Tree-addTopBtn', {
className={cx('Tree-addTopBtn', { 'is-disabled': isAdding || isEditing
'is-disabled': isAdding || isEditing })}
})} onClick={this.handleAdd.bind(this, null)}
onClick={this.handleAdd.bind(this, null)} >
> <Icon icon="plus" className="icon" />
<Icon icon="plus" className="icon" /> <span></span>
<span></span> </a>
</a>
</li>
); );
} }
return ( return (
<div className={cx(`Tree ${className || ''}`)}> <div className={cx(`Tree ${className || ''}`)}>
{data && data.length ? ( {options && options.length ? (
<ul className={cx('Tree-list')}> <ul className={cx('Tree-list')}>
{hideRoot ? ( {hideRoot ? (
<> <>
@ -687,34 +698,43 @@ export class TreeSelector extends React.Component<
{isAdding && !addingParent ? ( {isAdding && !addingParent ? (
<li className={cx('Tree-item')}>{this.renderInput()}</li> <li className={cx('Tree-item')}>{this.renderInput()}</li>
) : null} ) : null}
{this.renderList(data, value, false).dom} {this.renderList(options, value, false).dom}
</> </>
) : ( ) : (
<li <li
className={cx('Tree-item Tree-rootItem', { className={cx('Tree-rootItem', {
'is-checked': !value || !value.length 'is-checked': !value || !value.length
})} })}
> >
<a> <div className={cx('Tree-itemLabel')}>
{showIcon ? ( <span className={cx('Tree-itemText')}>
<i className={cx('Tree-itemIcon Tree-rootIcon')} /> {showIcon ? (
<i className={cx('Tree-itemIcon Tree-rootIcon')} />
) : null}
{rootLabel}
</span>
{!disabled &&
creatable &&
rootCreatable !== false &&
!isAdding &&
!isEditing ? (
<div className={cx('Tree-item-icons')}>
{creatable ? (
<a
onClick={this.handleAdd.bind(this, null)}
data-tooltip="添加一级节点"
>
<Icon icon="plus" className="icon" />
</a>
) : null}
</div>
) : null} ) : null}
</div>
<label className={cx('Tree-itemLabel')}>
<span
className={cx('Tree-itemText')}
onClick={this.clearSelect}
>
{rootLabel}
</span>
</label>
</a>
{addBtn}
<ul className={cx('Tree-sublist')}> <ul className={cx('Tree-sublist')}>
{isAdding && !addingParent ? ( {isAdding && !addingParent ? (
<li className={cx('Tree-item')}>{this.renderInput()}</li> <li className={cx('Tree-item')}>{this.renderInput()}</li>
) : null} ) : null}
{this.renderList(data, value, false).dom} {this.renderList(options, value, false).dom}
</ul> </ul>
</li> </li>
)} )}

View File

@ -67,6 +67,7 @@ export interface OptionsProps extends FormControlProps, OptionProps {
editControls?: Array<any>; editControls?: Array<any>;
deleteApi?: Api; deleteApi?: Api;
deleteConfirmText?: string; deleteConfirmText?: string;
optionLabel?: string;
} }
export function registerOptionsControl(config: OptionsConfig) { export function registerOptionsControl(config: OptionsConfig) {
@ -478,6 +479,7 @@ export function registerOptionsControl(config: OptionsConfig) {
disabled, disabled,
labelField, labelField,
onOpenDialog, onOpenDialog,
optionLabel,
addApi, addApi,
source, source,
data, data,
@ -509,7 +511,7 @@ export function registerOptionsControl(config: OptionsConfig) {
: await onOpenDialog( : await onOpenDialog(
{ {
type: 'dialog', type: 'dialog',
title: createBtnLabel || '新增选项', title: createBtnLabel || `新增${optionLabel || '选项'}`,
body: { body: {
type: 'form', type: 'form',
api: addApi, api: addApi,
@ -580,7 +582,8 @@ export function registerOptionsControl(config: OptionsConfig) {
editApi, editApi,
source, source,
data, data,
formItem: model formItem: model,
optionLabel
} = this.props; } = this.props;
if (disabled || !model) { if (disabled || !model) {
@ -603,7 +606,7 @@ export function registerOptionsControl(config: OptionsConfig) {
: await onOpenDialog( : await onOpenDialog(
{ {
type: 'dialog', type: 'dialog',
title: '编辑选项', title: `编辑${optionLabel || '选项'}`,
body: { body: {
type: 'form', type: 'form',
api: editApi, api: editApi,

View File

@ -15,6 +15,7 @@ export interface TreeProps extends OptionsControlProps {
onlyChildren?: boolean; // 选父级的时候,是否只把子节点的值包含在内 onlyChildren?: boolean; // 选父级的时候,是否只把子节点的值包含在内
addControls?: Array<any>; addControls?: Array<any>;
updateControls?: Array<any>; updateControls?: Array<any>;
rootCreatable?: boolean;
} }
export default class TreeControl extends React.Component<TreeProps> { export default class TreeControl extends React.Component<TreeProps> {
@ -63,7 +64,8 @@ export default class TreeControl extends React.Component<TreeProps> {
editable, editable,
editControls, editControls,
removable, removable,
onDelete onDelete,
rootCreatable
} = this.props; } = this.props;
return ( return (
@ -97,6 +99,7 @@ export default class TreeControl extends React.Component<TreeProps> {
selfDisabledAffectChildren={false} selfDisabledAffectChildren={false}
onAdd={onAdd} onAdd={onAdd}
creatable={creatable} creatable={creatable}
rootCreatable={rootCreatable}
onEdit={onEdit} onEdit={onEdit}
editable={editable} editable={editable}
removable={removable} removable={removable}

View File

@ -6,6 +6,7 @@ import PopOver from '../../components/PopOver';
import {OptionsControl, OptionsControlProps, Option} from './Options'; import {OptionsControl, OptionsControlProps, Option} from './Options';
import {Icon} from '../../components/icons'; import {Icon} from '../../components/icons';
import TreeSelector from '../../components/Tree'; import TreeSelector from '../../components/Tree';
// @ts-ignore
import matchSorter from 'match-sorter'; import matchSorter from 'match-sorter';
import debouce = require('lodash/debounce'); import debouce = require('lodash/debounce');
import find = require('lodash/find'); import find = require('lodash/find');

View File

@ -373,6 +373,19 @@ export function isDisabled(
); );
} }
export function hasAbility(
schema: any,
ability: string,
data?: object,
defaultValue: boolean = true
): boolean {
return schema.hasOwnProperty(ability)
? schema[ability]
: schema.hasOwnProperty(`${ability}On`)
? evalExpression(schema[`${ability}On`], data || schema)
: defaultValue;
}
export function makeHorizontalDeeper( export function makeHorizontalDeeper(
horizontal: { horizontal: {
left: string; left: string;