Tree 优化
This commit is contained in:
parent
4ac50f3bd7
commit
a4d07a5aec
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue