forked from p96170835/amis
修改 Tree 的增删改,并优化 tree 样式
This commit is contained in:
parent
fa35e964bd
commit
4ac50f3bd7
|
@ -146,6 +146,9 @@ $gap-base: px2rem(15px) !default;
|
|||
$gap-md: px2rem(20px) !default;
|
||||
$gap-lg: px2rem(30px) !default;
|
||||
|
||||
$icon-color: $gray600 !default;
|
||||
$icon-onHover-color: $gray900 !default;
|
||||
|
||||
// Components
|
||||
$scrollbar-width: px2rem(17px) !default;
|
||||
|
||||
|
@ -446,7 +449,7 @@ $Table-thead-fontSize: $fontSizeBase !default;
|
|||
$Table-thead-color: $text--loud-color !default;
|
||||
$Table-thead-borderColor: $Table-borderColor !default;
|
||||
$Table-thead-borderWidth: $Table-borderWidth !default;
|
||||
$Table-thead-iconColor: $text--muted-color !default;
|
||||
$Table-thead-iconColor: $icon-color !default;
|
||||
$TableCell-height: px2rem(40px) !default;
|
||||
$TableCell-paddingX: $gap-sm !default;
|
||||
$TableCell--edge-paddingX: $gap-md !default;
|
||||
|
@ -554,19 +557,19 @@ $ListItem-onModified-borderColor: darken($ListItem-onModified-bg, 10%) !default;
|
|||
$ListItem-onDragging-opacity: 0.1 !default;
|
||||
|
||||
// QuickEdit
|
||||
$QuickEdit-iconColor: $text--muted-color !default;
|
||||
$QuickEdit-onHover-iconColor: $text-color !default;
|
||||
$QuickEdit-iconColor: $icon-color !default;
|
||||
$QuickEdit-onHover-iconColor: $icon-onHover-color !default;
|
||||
$QuickEdit-onFocus-borderColor: $info !default;
|
||||
$QuickEdit-onFocus-borderWidth: $borderWidth !default;
|
||||
|
||||
// Copyable
|
||||
$Copyable-iconColor: $text--muted-color !default;
|
||||
$Copyable-onHover-iconColor: $text-color !default;
|
||||
$Copyable-iconColor: $icon-color !default;
|
||||
$Copyable-onHover-iconColor: $icon-onHover-color !default;
|
||||
|
||||
// PopOverAble
|
||||
|
||||
$PopOverAble-iconColor: $text--muted-color !default;
|
||||
$PopOverAble-onHover-iconColor: $text-color !default;
|
||||
$PopOverAble-iconColor: $icon-color !default;
|
||||
$PopOverAble-onHover-iconColor: $icon-onHover-color !default;
|
||||
|
||||
// PopOver
|
||||
$PopOver-bg: white !default;
|
||||
|
@ -574,8 +577,8 @@ $PopOver-bg: white !default;
|
|||
// Remark
|
||||
$Remark-width: 1rem !default;
|
||||
$Remark-icon-fontSize: $fontSizeBase !default;
|
||||
$Remark-iconColor: $text--muted-color !default;
|
||||
$Remark-onHover-iconColor: $text-color !default;
|
||||
$Remark-iconColor: $icon-color !default;
|
||||
$Remark-onHover-iconColor: $icon-onHover-color !default;
|
||||
$Remark-bg: transparent !default;
|
||||
$Remark-onHover-bg: transparent !default;
|
||||
$Remark-borderWidth: 0 !default;
|
||||
|
@ -618,7 +621,7 @@ $Form-input-onActive-color: $info !default;
|
|||
$Form-input-borderRadius: $borderRadius !default;
|
||||
$Form-input-borderColor: $borderColor !default;
|
||||
$Form-input-borderWidth: px2rem(1px) !default;
|
||||
$Form-input-iconColor: #999 !default;
|
||||
$Form-input-iconColor: $icon-color !default;
|
||||
$Form-input-onFocused-borderColor: $info !default;
|
||||
$Form-input-onFocused-bg: $Form-input-bg !default;
|
||||
$Form-input-onError-borderColor: $danger !default;
|
||||
|
@ -674,18 +677,18 @@ $Form-select-onFocused-color: $Form-select-color !default;
|
|||
$Form-select-onFocused-borderColor: $Form-input-onFocused-borderColor !default;
|
||||
$Form-select-onError-borderColor: $Form-input-onError-borderColor !default;
|
||||
$Form-selectOption-height: $Form-input-height !default;
|
||||
$Form-selectValue-color: #007eff !default;
|
||||
$Form-selectValue-color: $info !default;
|
||||
$Form-selectValue-bg: saturate(lighten($Form-selectValue-color, 40%), 2.5%) !default;
|
||||
$Form-selectValue-borderColor: saturate(lighten($Form-selectValue-color, 30%), 2.5%) !default;
|
||||
$Form-selectValue-fontSize: $fontSizeSm !default;
|
||||
$Form-select-caret-vender: 'FontAwesome' !default;
|
||||
$Form-select-caret-icon: '\f0d7' !default;
|
||||
$Form-select-caret-fontSize: $fontSizeBase !default;
|
||||
$Form-select-caret-iconColor: $Form-input-iconColor !default;
|
||||
$Form-select-caret-onHover-iconColor: $Form-input-iconColor !default;
|
||||
$Form-select-caret-iconColor: $icon-color !default;
|
||||
$Form-select-caret-onHover-iconColor: $icon-onHover-color !default;
|
||||
$Form-select-outer-borderWidth: px2rem(1px) !default;
|
||||
$Form-select-outer-boxShadow: none !default;
|
||||
$Form-select-outer-top: 100% !default;
|
||||
$Form-select-input-fontSize: $fontSizeSm !default;
|
||||
$Form-select-menu-height: $Form-input-height !default;
|
||||
$Form-select-menu-bg: $white !default;
|
||||
$Form-select-menu-color: $Form-select-color !default;
|
||||
|
@ -696,6 +699,8 @@ $Form-select-menu-onActive-bg: transparent !default;
|
|||
$Form-select-menu-onDisabled-color: $text--muted-color !default;
|
||||
$Form-select-menu-onDisabled-bg: transparent !default;
|
||||
$Form-select-checkall-bottomBorder: #eceff8 !default;
|
||||
$Form-select-popoverGap: 0 !default;
|
||||
$Form-select-search-height: $Form-select-menu-height !default;
|
||||
|
||||
// InputGroup
|
||||
$InputGroup-height: $Form-input-height !default;
|
||||
|
@ -713,8 +718,8 @@ $InputGroup-select-bg: $white !default;
|
|||
$InputGroup-select-onFocused-bg: $white !default;
|
||||
$InputGroup-select-color: $Form-select-color !default;
|
||||
$InputGroup-select-onFocused-color: $Form-select-onFocused-color !default;
|
||||
$InputGroup-select-arrowColor: $Form-select-caret-iconColor !default;
|
||||
$InputGroup-select-onFocused-arrowColor: $Form-select-caret-iconColor !default;
|
||||
$InputGroup-select-arrowColor: $icon-color !default;
|
||||
$InputGroup-select-onFocused-arrowColor: $icon-onHover-color !default;
|
||||
$InputGroup-button-borderWidth: $borderWidth !default;
|
||||
$InputGroup-button-borderColor: $Form-input-borderColor !default;
|
||||
$InputGroup-button-borderRadius: $borderRadius !default;
|
||||
|
@ -892,6 +897,8 @@ $Checkbox-color: $Form-input-borderColor !default;
|
|||
$Checkbox-onHover-color: $info !default;
|
||||
|
||||
$Checkbox--sm-size: px2rem(16px) !default;
|
||||
$Checkbox--sm-inner-size: $Checkbox--sm-size/2 !default;
|
||||
$Checkbox--sm--full-inner-size: px2rem(10px) !default;
|
||||
$Checkbox-gb: #fff !default;
|
||||
$Checkbox-borderRadius: $borderRadius !default;
|
||||
|
||||
|
@ -949,8 +956,8 @@ $DatePicker-fontSize: $Form-input-fontSize !default;
|
|||
$DatePicker-paddingX: px2rem(12px) !default;
|
||||
$DatePicker-paddingY: ($DatePicker-height - $DatePicker-lineHeight * $DatePicker-fontSize)/2 - $DatePicker-borderWidth !default;
|
||||
$DatePicker-placeholderColor: $Form-input-placeholderColor !default;
|
||||
$DatePicker-iconColor: $gray600 !default;
|
||||
$DatePicker-onHover-iconColor: darken($DatePicker-iconColor, 10%) !default;
|
||||
$DatePicker-iconColor: $icon-color !default;
|
||||
$DatePicker-onHover-iconColor: $icon-onHover-color !default;
|
||||
$DatePicker-onFocused-borderColor: $Form-input-onFocused-borderColor !default;
|
||||
$DatePicker-toggler-vendor: 'FontAwesome' !default;
|
||||
$DatePicker-toggler-fontSize: $Form-fontSize !default;
|
||||
|
@ -1041,7 +1048,7 @@ $ListControl-item-onDisabled-bg: $ListControl-item-bg !default;
|
|||
// Combo
|
||||
$Combo-toolbarBtn-lineHeight: 1 !default;
|
||||
$Combo-toolbarBtn-height: px2rem(20px) !default;
|
||||
$Combo-toolbarBtn-color: $Form-input-iconColor !default;
|
||||
$Combo-toolbarBtn-color: $icon-color !default;
|
||||
$Combo-toolbarBtn-paddingX: px2rem(5px) !default;
|
||||
$Combo-toolbarBtn-paddingY: px2rem(2px) !default;
|
||||
|
||||
|
@ -1261,7 +1268,7 @@ $TransferSelect-heading-borderBottom: $borderWidth solid $borderColor !default;
|
|||
// Tree
|
||||
$TreeSelect-popover-bg: #fff !default;
|
||||
$Tree-indent: px2rem(20px) !default;
|
||||
$Tree-itemArrowWidth: px2rem(10px) !default;
|
||||
$Tree-itemArrowWidth: px2rem(16px) !default;
|
||||
$Tree-arrowVendor: 'FontAwesome' !default;
|
||||
$Tree-unfoldedArrowContent: '\f107' !default;
|
||||
$Tree-foldedArrowContent: '\f105' !default;
|
||||
|
@ -1271,7 +1278,10 @@ $Tree-leafIconVendor: 'FontAwesome' !default;
|
|||
$Tree-leafIconContent: '\f15b' !default;
|
||||
$Tree-folderIconVendor: 'FontAwesome' !default;
|
||||
$Tree-folderIconContent: '\f07b' !default;
|
||||
$Tree-itemText--onChecked-color: $Form-selectValue-color !default;
|
||||
$Tree-itemLabel--onChecked-color: $Form-selectValue-color !default;
|
||||
$Tree-itemHeight: px2rem(30px) !default;
|
||||
$Tree-item-onHover-bg: rgba(0, 126, 255, 0.08) !default;
|
||||
$Tree-inputHeight: $Form-input-height * 0.85 !default;
|
||||
|
||||
// IconPicker
|
||||
$IconPicker-tabs-bg: #f0f3f4 !default;
|
||||
|
@ -1339,8 +1349,8 @@ $Carousel-imageTitle-bottom: px2rem(45px) !default;
|
|||
$Carousel-imageDescription-bottom: px2rem(25px) !default;
|
||||
|
||||
// Picker
|
||||
$Picker-iconColor: $gray600 !default;
|
||||
$Picker-onHover-iconColor: darken($Picker-iconColor, 10%) !default;
|
||||
$Picker-iconColor: $icon-color !default;
|
||||
$Picker-onHover-iconColor: $icon-onHover-color !default;
|
||||
$Picker-btn-vendor: 'FontAwesome' !default;
|
||||
$Picker-btn-fontSize: $Form-fontSize !default;
|
||||
$Picker-btn-icon: '\f2d2' !default;
|
|
@ -8,12 +8,12 @@
|
|||
position: absolute;
|
||||
}
|
||||
|
||||
&:hover input:not(:disabled) + i {
|
||||
&:hover input:not(:disabled)+i {
|
||||
border-color: $Checkbox-onHover-color;
|
||||
// box-shadow: 0 0 px2rem(1px) $Checkbox-onHover-color inset;
|
||||
}
|
||||
|
||||
> i {
|
||||
>i {
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
background: $Checkbox-gb;
|
||||
|
@ -21,7 +21,7 @@
|
|||
vertical-align: middle;
|
||||
position: relative;
|
||||
|
||||
+ span {
|
||||
+span {
|
||||
margin-left: $Checkbox-gap;
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -39,6 +39,8 @@
|
|||
height: 0px;
|
||||
background-color: transparent;
|
||||
transition: all 0.2s;
|
||||
transform-origin: 50% 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,19 +50,17 @@
|
|||
input {
|
||||
margin-left: -$Checkbox-size;
|
||||
|
||||
&:checked + i {
|
||||
&:checked+i {
|
||||
border-color: $Checkbox-onHover-color;
|
||||
|
||||
&:before {
|
||||
left: ($Checkbox-size - px2rem(2px) - $Checkbox-inner-size) / 2;
|
||||
top: ($Checkbox-size - px2rem(2px) - $Checkbox-inner-size) / 2;
|
||||
width: $Checkbox-inner-size;
|
||||
height: $Checkbox-inner-size;
|
||||
background: $Checkbox-onHover-color;
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled] + i {
|
||||
&[disabled]+i {
|
||||
border-color: lighten($Checkbox-color, 5%);
|
||||
cursor: not-allowed;
|
||||
|
||||
|
@ -70,13 +70,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
&[disabled] + i + span {
|
||||
&[disabled]+i+span {
|
||||
cursor: not-allowed;
|
||||
color: $text--muted-color;
|
||||
}
|
||||
}
|
||||
|
||||
> i {
|
||||
>i {
|
||||
width: $Checkbox-size;
|
||||
height: $Checkbox-size;
|
||||
border: px2rem(1px) solid $Checkbox-color;
|
||||
|
@ -87,25 +87,16 @@
|
|||
}
|
||||
|
||||
&--full.#{$ns}Checkbox--checkbox {
|
||||
&:not(:disabled) + i:hover {
|
||||
&:not(:disabled)+i:hover {
|
||||
border-color: $Checkbox-color;
|
||||
}
|
||||
|
||||
input {
|
||||
&:checked + i {
|
||||
&:checked+i {
|
||||
border-color: $Checkbox-onHover-color;
|
||||
background: $Checkbox-onHover-color;
|
||||
|
||||
&:before {
|
||||
// todo 后面自动计算
|
||||
@if $ns== 'cxd-' {
|
||||
top: px2rem(2px);
|
||||
left: px2rem(1px);
|
||||
} @else {
|
||||
top: px2rem(5px);
|
||||
left: ($Checkbox-size - $Checkbox--full-inner-size) /2;
|
||||
}
|
||||
|
||||
width: $Checkbox--full-inner-size;
|
||||
height: $Checkbox--full-inner-size / 2;
|
||||
border-color: $white;
|
||||
|
@ -120,13 +111,13 @@
|
|||
// }
|
||||
// }
|
||||
|
||||
&:checked[disabled] + i {
|
||||
&:checked[disabled]+i {
|
||||
border-color: lighten($Checkbox-color, 5%);
|
||||
background-color: lighten($Checkbox-color, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
> i {
|
||||
>i {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -138,9 +129,9 @@
|
|||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
transition: all 0.2s;
|
||||
transition: width 0.2s, height 0.2s, transform 0.2s;
|
||||
border-width: 0 0 px2rem(2px) px2rem(2px);
|
||||
transform: rotate(-40deg);
|
||||
transform: translate(-50%, -60%) rotate(-40deg);
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
@ -152,12 +143,10 @@
|
|||
input {
|
||||
margin-left: -$Radio-size;
|
||||
|
||||
&:checked + i {
|
||||
&:checked+i {
|
||||
border-color: $Radio-onHover-color;
|
||||
|
||||
&:before {
|
||||
left: ($Radio-size - px2rem(2px) - $Radio-inner-size) / 2;
|
||||
top: ($Radio-size - px2rem(2px) - $Radio-inner-size) / 2;
|
||||
width: $Radio-inner-size;
|
||||
height: $Radio-inner-size;
|
||||
background-color: $Radio-onHover-color;
|
||||
|
@ -165,7 +154,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&[disabled] + i {
|
||||
&[disabled]+i {
|
||||
border-color: lighten($Radio-color, 5%);
|
||||
cursor: not-allowed;
|
||||
|
||||
|
@ -174,13 +163,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
&[disabled] + i + span {
|
||||
&[disabled]+i+span {
|
||||
cursor: not-allowed;
|
||||
color: $text--muted-color;
|
||||
}
|
||||
}
|
||||
|
||||
> i {
|
||||
>i {
|
||||
cursor: pointer;
|
||||
width: $Radio-size;
|
||||
height: $Radio-size;
|
||||
|
@ -192,30 +181,41 @@
|
|||
}
|
||||
|
||||
&--sm {
|
||||
margin-right: px2rem(5px);
|
||||
padding-left: $Checkbox--sm-size;
|
||||
|
||||
input {
|
||||
&:checked + i {
|
||||
margin-left: -$Checkbox--sm-size;
|
||||
|
||||
&:checked+i {
|
||||
&:before {
|
||||
left: $Checkbox--sm-size / 4 - px2rem(1px);
|
||||
top: $Checkbox--sm-size / 4 - px2rem(1px);
|
||||
width: $Checkbox--sm-size / 2;
|
||||
height: $Checkbox--sm-size / 2;
|
||||
width: $Checkbox--sm-inner-size;
|
||||
height: $Checkbox--sm-inner-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> i {
|
||||
>i {
|
||||
width: $Checkbox--sm-size;
|
||||
height: $Checkbox--sm-size;
|
||||
margin-left: -$Checkbox--sm-size;
|
||||
|
||||
+ span {
|
||||
+span {
|
||||
margin-left: $gap-xs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--sm.#{$ns}Checkbox--full {
|
||||
input {
|
||||
&:checked+i {
|
||||
&:before {
|
||||
width: $Checkbox--sm--full-inner-size;
|
||||
height: $Checkbox--sm--full-inner-size / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-desc {
|
||||
color: $text--muted-color;
|
||||
margin-left: $Checkbox-gap;
|
||||
|
@ -244,13 +244,14 @@
|
|||
|
||||
.#{$ns}RadiosControl-group,
|
||||
.#{$ns}CheckboxesControl-group {
|
||||
|
||||
.#{$ns}RadiosControl-group,
|
||||
.#{$ns}CheckboxesControl-group {
|
||||
padding-left: px2rem(80px);
|
||||
@include clearfix();
|
||||
|
||||
> .#{$ns}RadiosControl-groupLabel,
|
||||
> .#{$ns}CheckboxesControl-groupLabel {
|
||||
>.#{$ns}RadiosControl-groupLabel,
|
||||
>.#{$ns}CheckboxesControl-groupLabel {
|
||||
float: left;
|
||||
width: px2rem(80px);
|
||||
margin-left: px2rem(-80px);
|
||||
|
|
|
@ -132,22 +132,10 @@
|
|||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
// &-menuOuter {
|
||||
// position: absolute;
|
||||
// background: $Form-select-menu-bg;
|
||||
// color: $Form-select-menu-color;
|
||||
// border: $Form-select-outer-borderWidth solid $Form-input-onFocused-borderColor;
|
||||
// left: px2rem(-1px);
|
||||
// right: px2rem(-1px);
|
||||
// min-width: 100%;
|
||||
// top: $Form-select-outer-top;
|
||||
// z-index: 10;
|
||||
// box-shadow: $Form-select-outer-boxShadow;
|
||||
// }
|
||||
|
||||
&-menu {
|
||||
max-height: px2rem(300px);
|
||||
overflow: auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
@ -156,19 +144,22 @@
|
|||
outline: none;
|
||||
border: none;
|
||||
margin: 0 $Form-select-paddingX;
|
||||
padding: ($Form-input-height - $Form-input-lineHeight * $Form-input-fontSize)/2 0;
|
||||
line-height: $Form-input-lineHeight;
|
||||
height: $Form-select-search-height;
|
||||
font-size: $Form-select-input-fontSize;
|
||||
border-bottom: 1px solid $borderColor;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// &.is-focused {
|
||||
// border-color: $Form-input-onFocused-borderColor;
|
||||
// }
|
||||
|
||||
>svg {
|
||||
fill: #999;
|
||||
width: px2rem(14px);
|
||||
min-width: px2rem(14px);
|
||||
height: px2rem(14px);
|
||||
margin-right: px2rem(5px);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
>input {
|
||||
|
@ -176,7 +167,8 @@
|
|||
border: none;
|
||||
flex-grow: 1;
|
||||
background: transparent;
|
||||
font-size: px2rem(12px);
|
||||
position: relative;
|
||||
top: 0.125em;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,8 +214,8 @@
|
|||
&-noResult {
|
||||
color: $Form-select-placeholderColor;
|
||||
line-height: $Form-input-lineHeight;
|
||||
font-size: $Form-select-input-fontSize;
|
||||
user-select: none;
|
||||
margin: 5px 0 0;
|
||||
padding: ($Form-select-menu-height - $Form-input-lineHeight * $Form-input-fontSize)/2 $Form-select-paddingX;
|
||||
}
|
||||
|
||||
|
@ -236,6 +228,10 @@
|
|||
cursor: pointer;
|
||||
padding: ($Form-select-menu-height - $Form-input-lineHeight * $Form-input-fontSize)/2 $Form-select-paddingX;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
>svg {
|
||||
width: px2rem(14px);
|
||||
height: px2rem(14px);
|
||||
|
@ -278,7 +274,8 @@
|
|||
}
|
||||
|
||||
.#{$ns}Select-popover {
|
||||
margin-top: px2rem(2px);
|
||||
margin-top: $Form-select-popoverGap - $Form-select-outer-borderWidth;
|
||||
|
||||
background: $Form-select-menu-bg;
|
||||
color: $Form-select-menu-color;
|
||||
border: $Form-select-outer-borderWidth solid $Form-input-onFocused-borderColor;
|
||||
|
@ -289,7 +286,7 @@
|
|||
z-index: 2;
|
||||
|
||||
&.#{$ns}PopOver--leftTopLeftBottom {
|
||||
margin-top: px2rem(-2px);
|
||||
margin-top: -($Form-select-popoverGap - $Form-select-outer-borderWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,34 +1,3 @@
|
|||
@mixin tree-input {
|
||||
> svg {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
width: px2rem(16px);
|
||||
height: px2rem(16px);
|
||||
margin-left: px2rem(5px);
|
||||
}
|
||||
|
||||
> input {
|
||||
margin-left: px2rem(15px);
|
||||
padding: px2rem(5px);
|
||||
width: px2rem(150px);
|
||||
height: px2rem(25px);
|
||||
color: $Form-input-color;
|
||||
|
||||
&::placeholder {
|
||||
color: $Form-input-placeholderColor;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border: $borderWidth solid $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
.#{$ns}TreeControl {
|
||||
border: 1px solid $Form-input-borderColor;
|
||||
padding: 6px 12px;
|
||||
|
@ -46,6 +15,7 @@
|
|||
}
|
||||
|
||||
.#{$ns}Tree {
|
||||
|
||||
&-list,
|
||||
&-sublist {
|
||||
list-style: none;
|
||||
|
@ -53,39 +23,26 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
&-sublist {
|
||||
padding-left: $Tree-indent;
|
||||
|
||||
&.is-folded {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> li {
|
||||
@include tree-input;
|
||||
}
|
||||
&-sublist.is-folded {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-item {
|
||||
line-height: px2rem(30px);
|
||||
line-height: $Tree-itemHeight;
|
||||
position: relative;
|
||||
|
||||
.#{$ns}Tree-item-icons {
|
||||
visibility: hidden;
|
||||
transition: visibility .1s ease;
|
||||
}
|
||||
|
||||
> a {
|
||||
>div {
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
||||
> span.#{$ns}Tree-item-icons {
|
||||
>.#{$ns}Tree-item-icons {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
> span > svg {
|
||||
>span>svg {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
@ -95,49 +52,105 @@
|
|||
margin-left: px2rem(5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--isLeaf > a {
|
||||
padding-left: $Tree-itemArrowWidth + $gap-xs;
|
||||
}
|
||||
|
||||
&--isEdit {
|
||||
display: inline-block;
|
||||
@include tree-input;
|
||||
&-itemLabel {
|
||||
&:hover {
|
||||
background: $Tree-item-onHover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&-rootItem {
|
||||
> a > i {
|
||||
margin-left: 0 !important;
|
||||
&-item-icons {
|
||||
visibility: hidden;
|
||||
transition: visibility .1s ease;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: $Tree-itemHeight;
|
||||
line-height: $Tree-itemHeight;
|
||||
|
||||
>a {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: $gap-xs;
|
||||
cursor: pointer;
|
||||
|
||||
>svg {
|
||||
$svgSize: px2rem(16px);
|
||||
width: $svgSize;
|
||||
height: $svgSize;
|
||||
top: 0.125 * $svgSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-itemInput {
|
||||
padding-left: $Tree-itemArrowWidth;
|
||||
|
||||
>a {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-left: $gap-sm;
|
||||
color: $icon-color;
|
||||
|
||||
&:hover {
|
||||
color: $icon-onHover-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Tree-addTop {
|
||||
height: px2rem(25px);
|
||||
line-height: px2rem(25px);
|
||||
cursor: pointer;
|
||||
padding-left: $Tree-indent;
|
||||
> p {
|
||||
> svg {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
width: px2rem(16px);
|
||||
height: px2rem(16px);
|
||||
}
|
||||
> span {
|
||||
padding-left: px2rem(5px);
|
||||
}
|
||||
>input {
|
||||
outline: none;
|
||||
background-color: $Form-input-bg;
|
||||
border: $Form-input-borderWidth solid $Form-input-borderColor;
|
||||
border-radius: $Form-input-borderRadius;
|
||||
line-height: $Form-input-lineHeight;
|
||||
padding: ($Tree-inputHeight - $Form-input-lineHeight * $Form-input-fontSize - px2rem(2px))/2 $Form-input-paddingX;
|
||||
font-size: $Form-input-fontSize;
|
||||
|
||||
&::placeholder {
|
||||
color: $Form-input-placeholderColor;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&-input {
|
||||
@include tree-input
|
||||
&:focus {
|
||||
border-color: $Form-input-onFocused-borderColor;
|
||||
box-shadow: $Form-input-boxShadow;
|
||||
|
||||
@if $Form-input-onFocused-bg !=$Form-input-bg {
|
||||
background-color: $Form-input-onFocused-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-addTopBtn {
|
||||
cursor: pointer;
|
||||
height: $Tree-itemHeight;
|
||||
line-height: $Tree-itemHeight;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
pointer-events: none;
|
||||
color: $text--muted-color;
|
||||
}
|
||||
|
||||
>svg {
|
||||
$svgSize: px2rem(14px);
|
||||
width: $svgSize;
|
||||
height: $svgSize;
|
||||
top: $svgSize * 0.125;
|
||||
margin-right: $Tree-itemArrowWidth - px2rem(14px); // icon 的宽度是14px
|
||||
}
|
||||
}
|
||||
|
||||
&-itemArrow {
|
||||
cursor: pointer;
|
||||
width: $Tree-itemArrowWidth;
|
||||
margin-right: $gap-xs;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
|
||||
&:before {
|
||||
|
@ -151,9 +164,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-itemArrowPlaceholder {
|
||||
display: inline-block;
|
||||
width: $Tree-itemArrowWidth;
|
||||
}
|
||||
|
||||
&-itemIcon {
|
||||
display: inline-block;
|
||||
margin-right: $gap-xs;
|
||||
margin-right: px2rem(3px);
|
||||
}
|
||||
|
||||
&-rootIcon {
|
||||
|
@ -180,13 +198,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-itemText {
|
||||
&-itemLabel {
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
&.is-checked,
|
||||
&.is-children-checked {
|
||||
color: $Tree-itemText--onChecked-color;
|
||||
color: $Tree-itemLabel--onChecked-color;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
|
@ -197,4 +215,46 @@
|
|||
&-placeholder {
|
||||
color: $text--muted-color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
&-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent;
|
||||
}
|
||||
|
||||
&-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 2;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 3;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 4;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 5;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 6;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 7;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 8;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 9;
|
||||
}
|
||||
|
||||
&-item &-item &-item &-item &-item &-item &-item &-item &-item &-item &-item>&-itemLabel {
|
||||
padding-left: $Tree-indent * 10;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,9 @@ $borderColor: #e8ebee;
|
|||
|
||||
$link-onHover-decoration: none;
|
||||
|
||||
$icon-color: #999;
|
||||
$icon-onHover-color: $primary;
|
||||
|
||||
$Layout-header-boxShadow: none;
|
||||
$Layout-header-bg: #F5F5F5;
|
||||
$Layout-aside-width: px2rem(180px);
|
||||
|
@ -103,6 +106,9 @@ $Form-select-menu-color: #333;
|
|||
$Form-select-menu-onHover-color: #000;
|
||||
$Form-select-menu-onHover-bg: #eaf6fe;
|
||||
$Form-select-menu-height: px2rem(24px);
|
||||
$Form-select-popoverGap: px2rem(3px);
|
||||
$Form-select-search-height: px2rem(30px);
|
||||
$Form-selectValue-color: $primary !default;
|
||||
|
||||
$InputGroup-select-borderWidth: px2rem(1px);
|
||||
$InputGroup-select-bg: #f6f7fb;
|
||||
|
|
|
@ -20,7 +20,7 @@ interface CheckboxProps {
|
|||
id?: string;
|
||||
key?: string | number;
|
||||
style?: React.CSSProperties;
|
||||
type?: string;
|
||||
type: 'checkbox' | 'radio';
|
||||
size?: 'sm' | 'lg' | 'small' | 'large';
|
||||
label?: string;
|
||||
labelClassName?: string;
|
||||
|
@ -80,19 +80,12 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
|||
labelClassName
|
||||
} = this.props;
|
||||
|
||||
className =
|
||||
(className ? className : '') +
|
||||
(size && sizeMap[size] ? ` ${sizeMap[size]}` : '');
|
||||
|
||||
return (
|
||||
<label
|
||||
className={cx(
|
||||
`Checkbox Checkbox--${type}`,
|
||||
{
|
||||
'Checkbox--full': !partial
|
||||
},
|
||||
className
|
||||
)}
|
||||
className={cx(`Checkbox Checkbox--${type}`, className, {
|
||||
'Checkbox--full': !partial,
|
||||
[`Checkbox--${size}`]: size
|
||||
})}
|
||||
>
|
||||
<input
|
||||
type={type}
|
||||
|
|
|
@ -47,11 +47,15 @@ export interface OptionProps {
|
|||
placeholder?: string;
|
||||
autoFill?: {[propName: string]: any};
|
||||
creatable?: boolean;
|
||||
onAdd?: (idx?: number) => void;
|
||||
onAdd?: (
|
||||
idx?: number | Array<number>,
|
||||
value?: any,
|
||||
skipForm?: boolean
|
||||
) => void;
|
||||
addControls?: Array<any>;
|
||||
editable?: boolean;
|
||||
editControls?: Array<any>;
|
||||
onEdit?: (value: Option) => void;
|
||||
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
|
||||
removable?: boolean;
|
||||
onDelete?: (value: Option) => void;
|
||||
}
|
||||
|
@ -588,7 +592,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
|||
const menu = (
|
||||
<div ref={this.menu} className={cx('Select-menu')}>
|
||||
{searchable ? (
|
||||
<div className={cx(`Select-input`)}>
|
||||
<div
|
||||
className={cx(`Select-input`, {
|
||||
'is-focused': this.state.isFocused
|
||||
})}
|
||||
>
|
||||
<Icon icon="search" className="icon" />
|
||||
<input
|
||||
{...getInputProps({
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {eachTree, isVisible, isObject, autobind} from '../utils/helper';
|
||||
import {eachTree, isVisible, autobind, findTreeIndex} from '../utils/helper';
|
||||
import {Option, Options, value2array} from './Checkboxes';
|
||||
import {ClassNamesFn, themeable} from '../theme';
|
||||
import {highlight} from '../renderers/Form/Options';
|
||||
import {Icon} from './icons';
|
||||
import Checkbox from './Checkbox';
|
||||
|
||||
interface TreeSelectorProps {
|
||||
classPrefix: string;
|
||||
|
@ -32,18 +33,19 @@ interface TreeSelectorProps {
|
|||
// 多选时,选中父节点时,是否只将起子节点加入到值中。
|
||||
onlyChildren?: boolean;
|
||||
// 名称、取值等字段名映射
|
||||
nameField?: string;
|
||||
valueField?: string;
|
||||
labelField: string;
|
||||
valueField: string;
|
||||
iconField?: string;
|
||||
unfoldedField?: string;
|
||||
foldedField?: string;
|
||||
disabledField?: string;
|
||||
|
||||
className?: string;
|
||||
itemClassName?: string;
|
||||
joinValues?: boolean;
|
||||
extractValue?: boolean;
|
||||
delimiter?: string;
|
||||
data: Options;
|
||||
options: Options;
|
||||
value: any;
|
||||
onChange: Function;
|
||||
placeholder?: string;
|
||||
|
@ -54,26 +56,30 @@ interface TreeSelectorProps {
|
|||
selfDisabledAffectChildren?: boolean;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
addMode?: 'dialog' | 'normal';
|
||||
addable?: boolean;
|
||||
onAdd?: Function;
|
||||
openAddDialog?: Function;
|
||||
editMode?: 'dialog' | 'normal';
|
||||
onEdit?: Function;
|
||||
|
||||
// 是否为内建 增、改、删。当有复杂表单的时候直接抛出去让外层能统一处理
|
||||
bultinCUD?: boolean;
|
||||
creatable?: boolean;
|
||||
onAdd?: (
|
||||
idx?: number | Array<number>,
|
||||
value?: any,
|
||||
skipForm?: boolean
|
||||
) => void;
|
||||
editable?: boolean;
|
||||
openEditDialog?: Function;
|
||||
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
|
||||
removable?: boolean;
|
||||
onRemove?: Function;
|
||||
onDelete?: (value: Option) => void;
|
||||
}
|
||||
|
||||
interface TreeSelectorState {
|
||||
value: Array<any>;
|
||||
unfolded: {[propName: string]: string};
|
||||
editItem: Option | null;
|
||||
addItem: Option | null;
|
||||
addingItem: Option | null;
|
||||
|
||||
inputValue: string;
|
||||
addingParent: Option | null;
|
||||
isAdding: boolean;
|
||||
isEditing: boolean;
|
||||
editingItem: Option | null;
|
||||
addTop: boolean;
|
||||
}
|
||||
|
||||
export class TreeSelector extends React.Component<
|
||||
|
@ -89,7 +95,7 @@ export class TreeSelector extends React.Component<
|
|||
disabled: false,
|
||||
withChildren: false,
|
||||
onlyChildren: false,
|
||||
nameField: 'name',
|
||||
labelField: 'label',
|
||||
valueField: 'value',
|
||||
iconField: 'icon',
|
||||
unfoldedField: 'unfolded',
|
||||
|
@ -115,14 +121,15 @@ export class TreeSelector extends React.Component<
|
|||
multiple: props.multiple,
|
||||
delimiter: props.delimiter,
|
||||
valueField: props.valueField,
|
||||
options: props.data
|
||||
options: props.options
|
||||
}),
|
||||
unfolded: this.syncUnFolded(props),
|
||||
editItem: null, // 点击编辑时的 item
|
||||
addItem: null, // 点击添加时的 item
|
||||
addingItem: null, // 添加后的 item
|
||||
editingItem: null, // 编辑后的 item
|
||||
addTop: false // 添加一级
|
||||
|
||||
inputValue: '',
|
||||
addingParent: null,
|
||||
isAdding: false,
|
||||
isEditing: false,
|
||||
editingItem: null
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -131,7 +138,7 @@ export class TreeSelector extends React.Component<
|
|||
|
||||
if (
|
||||
this.props.value !== nextProps.value ||
|
||||
this.props.data !== nextProps.data
|
||||
this.props.options !== nextProps.options
|
||||
) {
|
||||
toUpdate.value = value2array(nextProps.value, {
|
||||
joinValues: nextProps.joinValues,
|
||||
|
@ -139,11 +146,11 @@ export class TreeSelector extends React.Component<
|
|||
multiple: nextProps.multiple,
|
||||
delimiter: nextProps.delimiter,
|
||||
valueField: nextProps.valueField,
|
||||
options: nextProps.data
|
||||
options: nextProps.options
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.data !== nextProps.data) {
|
||||
if (this.props.options !== nextProps.options) {
|
||||
toUpdate.unfolded = this.syncUnFolded(nextProps);
|
||||
}
|
||||
|
||||
|
@ -155,7 +162,7 @@ export class TreeSelector extends React.Component<
|
|||
let unfolded: {[propName: string]: string} = {};
|
||||
const {foldedField, unfoldedField} = this.props;
|
||||
|
||||
eachTree(props.data, (node: Option, index, level) => {
|
||||
eachTree(props.options, (node: Option, index, level) => {
|
||||
if (node.children && node.children.length) {
|
||||
let ret: any = true;
|
||||
|
||||
|
@ -179,7 +186,6 @@ export class TreeSelector extends React.Component<
|
|||
@autobind
|
||||
toggleUnfolded(node: any) {
|
||||
this.setState({
|
||||
addItem: null,
|
||||
unfolded: {
|
||||
...this.state.unfolded,
|
||||
[node[this.props.valueField as string]]: !this.state.unfolded[
|
||||
|
@ -308,128 +314,124 @@ export class TreeSelector extends React.Component<
|
|||
}
|
||||
|
||||
@autobind
|
||||
handleAdd(item: Option | null, isFolder: boolean) {
|
||||
const {addMode, openAddDialog, valueField} = this.props;
|
||||
let {unfolded} = this.state;
|
||||
if (addMode === 'dialog') {
|
||||
openAddDialog && openAddDialog(item ? item : null);
|
||||
} else if (addMode === 'normal') {
|
||||
// item 为 null 时为添加一级
|
||||
if (item) {
|
||||
// 添加时,默认折叠的文件夹需要展开
|
||||
if (isFolder && !unfolded[item[valueField as string]]) {
|
||||
unfolded = {
|
||||
...unfolded,
|
||||
[item[valueField as string]]: !unfolded[item[valueField as string]]
|
||||
};
|
||||
}
|
||||
handleAdd(parent: Option | null = null) {
|
||||
const {bultinCUD, onAdd, options} = this.props;
|
||||
let idx: Array<number> | undefined = undefined;
|
||||
|
||||
this.setState({
|
||||
addItem: item,
|
||||
editItem: null,
|
||||
unfolded
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
addTop: true,
|
||||
editItem: null,
|
||||
addItem: null
|
||||
});
|
||||
}
|
||||
if (!bultinCUD) {
|
||||
idx = parent
|
||||
? findTreeIndex(options, item => item === parent)
|
||||
: undefined;
|
||||
return onAdd && onAdd(idx);
|
||||
} else {
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
isAdding: true,
|
||||
addingParent: parent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleEdit(item: Option) {
|
||||
const {editMode, openEditDialog} = this.props;
|
||||
const {addItem} = this.state;
|
||||
if (editMode === 'dialog') {
|
||||
openEditDialog && openEditDialog(item);
|
||||
addItem &&
|
||||
this.setState({
|
||||
addItem: null
|
||||
});
|
||||
} else if (editMode === 'normal') {
|
||||
this.setState({
|
||||
editItem: item,
|
||||
addItem: null
|
||||
});
|
||||
}
|
||||
const labelField = this.props.labelField;
|
||||
this.setState({
|
||||
isEditing: true,
|
||||
isAdding: false,
|
||||
editingItem: item,
|
||||
inputValue: item[labelField]
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleRemove(item: Option) {
|
||||
const {onRemove} = this.props;
|
||||
onRemove && onRemove(item);
|
||||
const {onDelete} = this.props;
|
||||
|
||||
onDelete && onDelete(item);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleConfirmOnAdd() {
|
||||
const {onAdd} = this.props;
|
||||
const {addItem: parent, addingItem} = this.state;
|
||||
onAdd &&
|
||||
onAdd({
|
||||
...addingItem,
|
||||
parent: parent
|
||||
});
|
||||
|
||||
handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.setState({
|
||||
addingItem: null,
|
||||
addItem: null,
|
||||
addTop: false
|
||||
inputValue: e.currentTarget.value
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleCancelOnAdd() {
|
||||
this.setState({
|
||||
addItem: null,
|
||||
addTop: false
|
||||
});
|
||||
}
|
||||
handleConfirm() {
|
||||
const {
|
||||
inputValue: value,
|
||||
isAdding,
|
||||
addingParent,
|
||||
editingItem,
|
||||
isEditing
|
||||
} = this.state;
|
||||
|
||||
@autobind
|
||||
handleConfirmOnEdit() {
|
||||
const {onEdit} = this.props;
|
||||
let {editingItem, editItem: prevItem} = this.state;
|
||||
onEdit &&
|
||||
onEdit({
|
||||
...editingItem,
|
||||
prev: prevItem
|
||||
});
|
||||
this.setState({
|
||||
editingItem: null,
|
||||
editItem: null
|
||||
});
|
||||
}
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleCancelOnEdit() {
|
||||
this.setState({
|
||||
editItem: null
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChangeOnAdd(value: string) {
|
||||
this.setState({
|
||||
addingItem: {
|
||||
label: value
|
||||
const {labelField, onAdd, options, onEdit} = this.props;
|
||||
this.setState(
|
||||
{
|
||||
inputValue: '',
|
||||
isAdding: false,
|
||||
isEditing: false
|
||||
},
|
||||
() => {
|
||||
if (isAdding && onAdd) {
|
||||
let idx =
|
||||
(addingParent &&
|
||||
findTreeIndex(options, item => item === addingParent)) ||
|
||||
[];
|
||||
onAdd(idx.concat(0), {[labelField]: value}, true);
|
||||
} else if (isEditing && onEdit) {
|
||||
onEdit(
|
||||
{
|
||||
...editingItem,
|
||||
[labelField]: value
|
||||
},
|
||||
editingItem!,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleChangeOnEdit(item: Option, value: string) {
|
||||
let {editItem} = this.state;
|
||||
handleCancel() {
|
||||
this.setState({
|
||||
editingItem: {
|
||||
...item,
|
||||
label: value || (editItem as Option)['label']
|
||||
}
|
||||
inputValue: '',
|
||||
isAdding: false,
|
||||
isEditing: false
|
||||
});
|
||||
}
|
||||
|
||||
renderInput(prfix: JSX.Element | null = null) {
|
||||
const {classnames: cx} = this.props;
|
||||
const {inputValue} = this.state;
|
||||
|
||||
return (
|
||||
<div className={cx('Tree-itemLabel')}>
|
||||
<div className={cx('Tree-itemInput')}>
|
||||
{prfix}
|
||||
<input
|
||||
onChange={this.handleInputChange}
|
||||
value={inputValue}
|
||||
placeholder="请输入"
|
||||
/>
|
||||
<a data-tooltip="取消" onClick={this.handleCancel}>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
<a data-tooltip="确认" onClick={this.handleConfirm}>
|
||||
<Icon icon="check" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@autobind
|
||||
renderList(
|
||||
list: Options,
|
||||
|
@ -442,7 +444,7 @@ export class TreeSelector extends React.Component<
|
|||
showRadio,
|
||||
multiple,
|
||||
disabled,
|
||||
nameField = '',
|
||||
labelField: nameField = '',
|
||||
valueField = '',
|
||||
iconField = '',
|
||||
disabledField = '',
|
||||
|
@ -451,14 +453,21 @@ export class TreeSelector extends React.Component<
|
|||
onlyChildren,
|
||||
classnames: cx,
|
||||
highlightTxt,
|
||||
data,
|
||||
options: data,
|
||||
maxLength,
|
||||
minLength,
|
||||
addable,
|
||||
creatable,
|
||||
editable,
|
||||
removable
|
||||
} = this.props;
|
||||
const {addItem, editItem, unfolded, addTop, value: stateValue} = this.state;
|
||||
const {
|
||||
unfolded,
|
||||
value: stateValue,
|
||||
isAdding,
|
||||
addingParent,
|
||||
editingItem,
|
||||
isEditing
|
||||
} = this.state;
|
||||
|
||||
let childrenChecked = 0;
|
||||
let ret = list.map((item, key) => {
|
||||
|
@ -508,25 +517,19 @@ export class TreeSelector extends React.Component<
|
|||
}
|
||||
|
||||
const checkbox: JSX.Element | null = multiple ? (
|
||||
<label className={cx(`Checkbox Checkbox--checkbox Checkbox--sm`)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
disabled={nodeDisabled}
|
||||
checked={selfChecked}
|
||||
onChange={e => this.handleCheck(item, e.currentTarget.checked)}
|
||||
/>
|
||||
<i />
|
||||
</label>
|
||||
<Checkbox
|
||||
size="sm"
|
||||
disabled={nodeDisabled}
|
||||
checked={checked}
|
||||
onChange={this.handleCheck.bind(this, item)}
|
||||
/>
|
||||
) : showRadio ? (
|
||||
<label className={cx(`Checkbox Checkbox--radio Checkbox--sm`)}>
|
||||
<input
|
||||
type="radio"
|
||||
disabled={nodeDisabled}
|
||||
checked={checked}
|
||||
onChange={() => this.handleSelect(item)}
|
||||
/>
|
||||
<i />
|
||||
</label>
|
||||
<Checkbox
|
||||
size="sm"
|
||||
disabled={nodeDisabled}
|
||||
checked={checked}
|
||||
onChange={this.handleSelect.bind(this, item)}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const isLeaf = !item.children || !item.children.length;
|
||||
|
@ -538,10 +541,17 @@ export class TreeSelector extends React.Component<
|
|||
'Tree-item--isLeaf': isLeaf
|
||||
})}
|
||||
>
|
||||
{!editItem ||
|
||||
(isObject(editItem) &&
|
||||
(editItem as Option)[valueField] !== item[valueField]) ? (
|
||||
<a>
|
||||
{isEditing && editingItem === item ? (
|
||||
this.renderInput(checkbox)
|
||||
) : (
|
||||
<div
|
||||
className={cx('Tree-itemLabel', {
|
||||
'is-children-checked':
|
||||
multiple && !cascade && tmpChildrenChecked && !nodeDisabled,
|
||||
'is-checked': checked,
|
||||
'is-disabled': nodeDisabled
|
||||
})}
|
||||
>
|
||||
{!isLeaf ? (
|
||||
<i
|
||||
onClick={() => this.toggleUnfolded(item)}
|
||||
|
@ -549,26 +559,14 @@ export class TreeSelector extends React.Component<
|
|||
'is-folded': !unfolded[item[valueField]]
|
||||
})}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{showIcon ? (
|
||||
<i
|
||||
className={cx(
|
||||
`Tree-itemIcon ${item[iconField] ||
|
||||
(childrenItems ? 'Tree-folderIcon' : 'Tree-leafIcon')}`
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
) : (
|
||||
<span className={cx('Tree-itemArrowPlaceholder')} />
|
||||
)}
|
||||
|
||||
{checkbox}
|
||||
|
||||
<span
|
||||
className={cx('Tree-itemText', {
|
||||
'is-children-checked':
|
||||
multiple && !cascade && tmpChildrenChecked && !nodeDisabled,
|
||||
'is-checked': checked,
|
||||
'is-disabled': nodeDisabled
|
||||
})}
|
||||
className={cx('Tree-itemText')}
|
||||
onClick={() =>
|
||||
!nodeDisabled &&
|
||||
(multiple
|
||||
|
@ -576,77 +574,63 @@ export class TreeSelector extends React.Component<
|
|||
: this.handleSelect(item))
|
||||
}
|
||||
>
|
||||
{showIcon ? (
|
||||
<i
|
||||
className={cx(
|
||||
`Tree-itemIcon ${item[iconField] ||
|
||||
(childrenItems ? 'Tree-folderIcon' : 'Tree-leafIcon')}`
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{highlightTxt
|
||||
? highlight(item[nameField], highlightTxt)
|
||||
: item[nameField]}
|
||||
</span>
|
||||
{!nodeDisabled && !addTop && !addItem && !editItem ? (
|
||||
<span className={cx('Tree-item-icons')}>
|
||||
{addable ? (
|
||||
<Icon
|
||||
icon="plus"
|
||||
className="icon"
|
||||
onClick={() => this.handleAdd(item, !isLeaf)}
|
||||
/>
|
||||
{!nodeDisabled && !isAdding && !isEditing ? (
|
||||
<div className={cx('Tree-item-icons')}>
|
||||
{creatable ? (
|
||||
<a
|
||||
onClick={this.handleAdd.bind(this, item)}
|
||||
data-tooltip="添加孩子节点"
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
{removable ? (
|
||||
<Icon
|
||||
icon="minus"
|
||||
className="icon"
|
||||
onClick={() => this.handleRemove(item)}
|
||||
/>
|
||||
<a
|
||||
onClick={this.handleRemove.bind(this, item)}
|
||||
data-tooltip="移除该节点"
|
||||
>
|
||||
<Icon icon="minus" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
{editable ? (
|
||||
<Icon
|
||||
icon="pencil"
|
||||
className="icon"
|
||||
onClick={() => this.handleEdit(item)}
|
||||
/>
|
||||
<a
|
||||
onClick={this.handleEdit.bind(this, item)}
|
||||
data-tooltip="编辑该节点"
|
||||
>
|
||||
<Icon icon="pencil" className="icon" />
|
||||
</a>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</a>
|
||||
) : (
|
||||
<div className={cx('Tree-item--isEdit')}>
|
||||
<input
|
||||
defaultValue={item['label']}
|
||||
onChange={e =>
|
||||
this.handleChangeOnEdit(item, e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
<Icon
|
||||
icon="check"
|
||||
className="icon"
|
||||
onClick={this.handleConfirmOnEdit}
|
||||
/>
|
||||
<Icon
|
||||
icon="close"
|
||||
className="icon"
|
||||
onClick={this.handleCancelOnEdit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* 有children而且为展开状态 或者 添加child时 */}
|
||||
{(childrenItems && unfolded[item[valueField]]) ||
|
||||
(addItem && addItem[valueField] === item[valueField]) ? (
|
||||
(isAdding && addingParent === item) ? (
|
||||
<ul className={cx('Tree-sublist')}>
|
||||
{addItem && addItem[valueField] === item[valueField] ? (
|
||||
<li>
|
||||
<input
|
||||
onChange={e =>
|
||||
this.handleChangeOnAdd(e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
<Icon
|
||||
icon="check"
|
||||
className="icon"
|
||||
onClick={this.handleConfirmOnAdd}
|
||||
/>
|
||||
<Icon
|
||||
icon="close"
|
||||
className="icon"
|
||||
onClick={this.handleCancelOnAdd}
|
||||
/>
|
||||
{isAdding && addingParent === item ? (
|
||||
<li className={cx('Tree-item')}>
|
||||
{this.renderInput(
|
||||
checkbox
|
||||
? React.cloneElement(checkbox, {
|
||||
checked: false,
|
||||
disabled: true
|
||||
})
|
||||
: null
|
||||
)}
|
||||
</li>
|
||||
) : null}
|
||||
{childrenItems}
|
||||
|
@ -670,29 +654,53 @@ export class TreeSelector extends React.Component<
|
|||
rootLabel,
|
||||
showIcon,
|
||||
classnames: cx,
|
||||
addable
|
||||
creatable
|
||||
} = this.props;
|
||||
let data = this.props.data;
|
||||
const {value, addTop} = this.state;
|
||||
let data = this.props.options;
|
||||
const {value, isAdding, addingParent, isEditing, inputValue} = this.state;
|
||||
|
||||
let addBtn = null;
|
||||
|
||||
if (creatable) {
|
||||
addBtn = (
|
||||
<li>
|
||||
<a
|
||||
className={cx('Tree-addTopBtn', {
|
||||
'is-disabled': isAdding || isEditing
|
||||
})}
|
||||
onClick={this.handleAdd.bind(this, null)}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>添加一级节点</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(`Tree ${className || ''}`)}>
|
||||
{data && data.length ? (
|
||||
<ul className={cx('Tree-list')}>
|
||||
{hideRoot ? (
|
||||
this.renderList(data, value, false).dom
|
||||
<>
|
||||
{addBtn}
|
||||
{isAdding && !addingParent ? (
|
||||
<li className={cx('Tree-item')}>{this.renderInput()}</li>
|
||||
) : null}
|
||||
{this.renderList(data, value, false).dom}
|
||||
</>
|
||||
) : (
|
||||
<li className={cx('Tree-item Tree-rootItem')}>
|
||||
<li
|
||||
className={cx('Tree-item Tree-rootItem', {
|
||||
'is-checked': !value || !value.length
|
||||
})}
|
||||
>
|
||||
<a>
|
||||
{showIcon ? (
|
||||
<i className={cx('Tree-itemIcon Tree-rootIcon')} />
|
||||
) : null}
|
||||
|
||||
<label
|
||||
className={cx('Tree-itemLabel', {
|
||||
'is-checked': !value || !value.length
|
||||
})}
|
||||
>
|
||||
<label className={cx('Tree-itemLabel')}>
|
||||
<span
|
||||
className={cx('Tree-itemText')}
|
||||
onClick={this.clearSelect}
|
||||
|
@ -701,36 +709,11 @@ export class TreeSelector extends React.Component<
|
|||
</span>
|
||||
</label>
|
||||
</a>
|
||||
{addable ? (
|
||||
<div className={cx('Tree-addTop')}>
|
||||
{!addTop ? (
|
||||
<p onClick={() => this.handleAdd(null, false)}>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>添加一级</span>
|
||||
</p>
|
||||
) : null}
|
||||
{addTop ? (
|
||||
<div className={cx('Tree-addTop-input')}>
|
||||
<input
|
||||
onChange={e =>
|
||||
this.handleChangeOnAdd(e.currentTarget.value)
|
||||
}
|
||||
/>
|
||||
<Icon
|
||||
icon="check"
|
||||
className="icon"
|
||||
onClick={this.handleConfirmOnAdd}
|
||||
/>
|
||||
<Icon
|
||||
icon="close"
|
||||
className="icon"
|
||||
onClick={this.handleCancelOnAdd}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{addBtn}
|
||||
<ul className={cx('Tree-sublist')}>
|
||||
{isAdding && !addingParent ? (
|
||||
<li className={cx('Tree-item')}>{this.renderInput()}</li>
|
||||
) : null}
|
||||
{this.renderList(data, value, false).dom}
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3506" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path d="M972.544 175.189333a31.658667 31.658667 0 0 1 15.701333 5.162667 31.872 31.872 0 0 1 7.296 46.976c-0.682667 0.896-1.493333 1.664-2.218666 2.474667L343.082667 876.032a32.341333 32.341333 0 0 1-37.546667 5.589333 37.504 37.504 0 0 1-8.064-6.101333L30.208 597.845333c-0.768-0.810667-1.536-1.621333-2.218667-2.517333a32.256 32.256 0 0 1 14.378667-49.749333 32 32 0 0 1 28.8 3.584c2.474667 1.664 2.944 2.304 5.12 4.309333l244.736 254.250667L948.181333 184.448l2.517334-2.261333a36.693333 36.693333 0 0 1 11.861333-6.016c2.901333-0.725333 3.669333-0.725333 6.613333-1.024l3.370667 0.042666z" p-id="3507"></path>
|
||||
<svg viewBox="0 0 13 9" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="3506"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g transform="translate(6.656854, 2.656854) scale(-1, 1) rotate(-315.000000) translate(-6.656854, -2.656854) ">
|
||||
<polygon id="path-1" points="11.1568542 5.15685425 11.1568542 -0.843145751 12.1568542 -0.843145751 12.1568542 6.15685425 1.15685425 6.15685425 1.15685425 5.15685425"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 759 B After Width: | Height: | Size: 458 B |
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" version="1.1" p-id="1463">
|
||||
<path d="M967.81435 106.836237 917.16274 56.18565 512 461.34839 106.836237 56.18565 56.184627 106.836237 461.34839 512 56.184627 917.163763 106.836237 967.815373 512 562.65161 917.16274 967.815373 967.81435 917.163763 562.650587 512Z" p-id="1464" data-spm-anchor-id="a313x.7781069.0.i0" />
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12 12" version="1.1">
|
||||
<polygon id="path-1" points="6.0003653 5.2970518 10.5993691 0.6980479600000002 11.3064759 1.4051547400000004 6.7074721 6.0041586 11.3009516 10.5976381 10.5938448 11.3047449 6.0003653 6.7112654 1.4056713299999997 11.3059593 0.6985645500000004 10.5988525 5.2932585 6.0041586 0.6956119200000002 1.4065120000000002 1.4027187000000003 0.69940522"></polygon>
|
||||
</svg>
|
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 486 B |
|
@ -8,7 +8,9 @@ import {
|
|||
anyChanged,
|
||||
autobind,
|
||||
createObject,
|
||||
setVariable
|
||||
setVariable,
|
||||
spliceTree,
|
||||
findTreeIndex
|
||||
} from '../../utils/helper';
|
||||
import {reaction} from 'mobx';
|
||||
import {FormControlProps, registerFormItem, FormItemBasicConfig} from './Item';
|
||||
|
@ -42,11 +44,15 @@ export interface OptionsControlProps extends FormControlProps, OptionProps {
|
|||
setLoading: (value: boolean) => void;
|
||||
reloadOptions: () => void;
|
||||
creatable?: boolean;
|
||||
onAdd?: (idx?: number) => void;
|
||||
onAdd?: (
|
||||
idx?: number | Array<number>,
|
||||
value?: any,
|
||||
skipForm?: boolean
|
||||
) => void;
|
||||
addControls?: Array<any>;
|
||||
editable?: boolean;
|
||||
editControls?: Array<any>;
|
||||
onEdit?: (value: Option) => void;
|
||||
onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
|
||||
removable?: boolean;
|
||||
onDelete?: (value: Option) => void;
|
||||
}
|
||||
|
@ -462,7 +468,11 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
}
|
||||
|
||||
@autobind
|
||||
async handleOptionAdd(idx: number = -1) {
|
||||
async handleOptionAdd(
|
||||
idx: number | Array<number> = -1,
|
||||
value: any,
|
||||
skipForm: boolean = false
|
||||
) {
|
||||
let {
|
||||
addControls,
|
||||
disabled,
|
||||
|
@ -473,15 +483,17 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
data,
|
||||
valueField,
|
||||
formItem: model,
|
||||
createBtnLabel
|
||||
createBtnLabel,
|
||||
env
|
||||
} = this.props;
|
||||
|
||||
// 禁用或者没有配置 name
|
||||
if (disabled || !model) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 用户没有配置表单项,则自动创建一个 label 输入
|
||||
if (!Array.isArray(addControls) || !addControls.length) {
|
||||
if (!skipForm && (!Array.isArray(addControls) || !addControls.length)) {
|
||||
addControls = [
|
||||
{
|
||||
type: 'text',
|
||||
|
@ -492,18 +504,37 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
];
|
||||
}
|
||||
|
||||
let result: any = await onOpenDialog(
|
||||
{
|
||||
type: 'dialog',
|
||||
title: createBtnLabel || '新增选项',
|
||||
body: {
|
||||
type: 'form',
|
||||
api: addApi,
|
||||
controls: addControls
|
||||
let result: any = skipForm
|
||||
? value
|
||||
: await onOpenDialog(
|
||||
{
|
||||
type: 'dialog',
|
||||
title: createBtnLabel || '新增选项',
|
||||
body: {
|
||||
type: 'form',
|
||||
api: addApi,
|
||||
controls: addControls
|
||||
}
|
||||
},
|
||||
data
|
||||
);
|
||||
|
||||
// 单独发请求
|
||||
if (skipForm && addApi) {
|
||||
try {
|
||||
const payload = await env.fetcher(addApi!, result);
|
||||
|
||||
if (!payload.ok) {
|
||||
env.notify('error', payload.msg || '新增失败,请仔细检查');
|
||||
} else {
|
||||
result = payload.data || result;
|
||||
}
|
||||
},
|
||||
data
|
||||
);
|
||||
} catch (e) {
|
||||
result = null;
|
||||
console.error(e);
|
||||
env.notify('error', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 有 result 说明弹框点了确认。否则就是取消了。
|
||||
if (!result) {
|
||||
|
@ -523,13 +554,24 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
this.reload();
|
||||
} else {
|
||||
// 否则直接前端变更 options
|
||||
const options = model.options.concat();
|
||||
~idx ? options.splice(idx, 0, {...result}) : options.push({...result});
|
||||
let options = model.options.concat();
|
||||
if (Array.isArray(idx)) {
|
||||
options = spliceTree(options, idx, 0, {...result});
|
||||
} else {
|
||||
~idx
|
||||
? options.splice(idx, 0, {...result})
|
||||
: options.push({...result});
|
||||
}
|
||||
model.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
async handleOptionEdit(value: any) {
|
||||
async handleOptionEdit(
|
||||
value: any,
|
||||
origin: any = value,
|
||||
skipForm: boolean = false
|
||||
) {
|
||||
let {
|
||||
editControls,
|
||||
disabled,
|
||||
|
@ -538,15 +580,14 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
editApi,
|
||||
source,
|
||||
data,
|
||||
formItem: model,
|
||||
valueField
|
||||
formItem: model
|
||||
} = this.props;
|
||||
|
||||
if (disabled || !model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(editControls) || !editControls.length) {
|
||||
if (!skipForm && (!Array.isArray(editControls) || !editControls.length)) {
|
||||
editControls = [
|
||||
{
|
||||
type: 'text',
|
||||
|
@ -557,18 +598,20 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
];
|
||||
}
|
||||
|
||||
let result = await onOpenDialog(
|
||||
{
|
||||
type: 'dialog',
|
||||
title: '编辑选项',
|
||||
body: {
|
||||
type: 'form',
|
||||
api: editApi,
|
||||
controls: editControls
|
||||
}
|
||||
},
|
||||
createObject(data, value)
|
||||
);
|
||||
let result = skipForm
|
||||
? value
|
||||
: await onOpenDialog(
|
||||
{
|
||||
type: 'dialog',
|
||||
title: '编辑选项',
|
||||
body: {
|
||||
type: 'form',
|
||||
api: editApi,
|
||||
controls: editControls
|
||||
}
|
||||
},
|
||||
createObject(data, value)
|
||||
);
|
||||
|
||||
// 没有结果,说明取消了。
|
||||
if (!result) {
|
||||
|
@ -578,18 +621,15 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
if (source) {
|
||||
this.reload();
|
||||
} else {
|
||||
const options = model.options.concat();
|
||||
const idx = findIndex(
|
||||
options,
|
||||
item => item[valueField || 'value'] == result[valueField || 'value']
|
||||
);
|
||||
const indexes = findTreeIndex(model.options, item => item === origin);
|
||||
|
||||
if (~idx) {
|
||||
options.splice(idx, 1, {
|
||||
...options[idx],
|
||||
...result
|
||||
});
|
||||
model.setOptions(options);
|
||||
if (indexes) {
|
||||
model.setOptions(
|
||||
spliceTree(model.options, indexes, 1, {
|
||||
...origin,
|
||||
...result
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -623,6 +663,10 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
|
||||
// 通过 deleteApi 删除。
|
||||
try {
|
||||
if (!deleteApi) {
|
||||
throw new Error('请配置 deleteApi');
|
||||
}
|
||||
|
||||
const result = await env.fetcher(deleteApi!, ctx);
|
||||
|
||||
if (!result.ok) {
|
||||
|
@ -655,7 +699,8 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
editApi,
|
||||
deleteApi,
|
||||
creatable,
|
||||
editable
|
||||
editable,
|
||||
removable
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -673,7 +718,7 @@ export function registerOptionsControl(config: OptionsConfig) {
|
|||
reloadOptions={this.reload}
|
||||
creatable={creatable || isEffectiveApi(addApi)}
|
||||
editable={editable || isEffectiveApi(editApi)}
|
||||
removable={isEffectiveApi(deleteApi)}
|
||||
removable={removable || isEffectiveApi(deleteApi)}
|
||||
onAdd={this.handleOptionAdd}
|
||||
onEdit={this.handleOptionEdit}
|
||||
onDelete={this.handleOptionDelete}
|
||||
|
|
|
@ -2,11 +2,6 @@ import React from 'react';
|
|||
import cx from 'classnames';
|
||||
import TreeSelector from '../../components/Tree';
|
||||
import {OptionsControl, OptionsControlProps} from './Options';
|
||||
import {autobind, createObject} from '../../utils/helper';
|
||||
import {Action, Schema, PlainObject, Api, Payload} from '../../types';
|
||||
import {isEffectiveApi} from '../../utils/api';
|
||||
import {filter} from '../../utils/tpl';
|
||||
import {Option} from '../../components/Checkboxes';
|
||||
import {Spinner} from '../../components';
|
||||
|
||||
export interface TreeProps extends OptionsControlProps {
|
||||
|
@ -18,13 +13,14 @@ export interface TreeProps extends OptionsControlProps {
|
|||
cascade?: boolean; // 父子之间是否完全独立。
|
||||
withChildren?: boolean; // 选父级的时候是否把子节点的值也包含在内。
|
||||
onlyChildren?: boolean; // 选父级的时候,是否只把子节点的值包含在内
|
||||
addControls?: Array<any>;
|
||||
updateControls?: Array<any>;
|
||||
}
|
||||
|
||||
export default class TreeControl extends React.Component<TreeProps> {
|
||||
static defaultProps: Partial<TreeProps> = {
|
||||
placeholder: '选项加载中...',
|
||||
multiple: false,
|
||||
hideRoot: false,
|
||||
rootLabel: '顶级',
|
||||
rootValue: '',
|
||||
showIcon: true
|
||||
|
@ -59,7 +55,15 @@ export default class TreeControl extends React.Component<TreeProps> {
|
|||
cascade,
|
||||
rootValue,
|
||||
showIcon,
|
||||
showRadio
|
||||
showRadio,
|
||||
onAdd,
|
||||
creatable,
|
||||
addControls,
|
||||
onEdit,
|
||||
editable,
|
||||
editControls,
|
||||
removable,
|
||||
onDelete
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -75,7 +79,7 @@ export default class TreeControl extends React.Component<TreeProps> {
|
|||
extractValue={extractValue}
|
||||
delimiter={delimiter}
|
||||
placeholder={placeholder}
|
||||
data={options}
|
||||
options={options}
|
||||
multiple={multiple}
|
||||
initiallyOpen={initiallyOpen}
|
||||
unfoldedLevel={unfoldedLevel}
|
||||
|
@ -89,8 +93,15 @@ export default class TreeControl extends React.Component<TreeProps> {
|
|||
cascade={cascade}
|
||||
foldedField="collapsed"
|
||||
value={value || ''}
|
||||
nameField="label"
|
||||
labelField="label"
|
||||
selfDisabledAffectChildren={false}
|
||||
onAdd={onAdd}
|
||||
creatable={creatable}
|
||||
onEdit={onEdit}
|
||||
editable={editable}
|
||||
removable={removable}
|
||||
onDelete={onDelete}
|
||||
bultinCUD={!addControls && !editControls}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -419,7 +419,7 @@ export default class TreeSelectControl extends React.Component<
|
|||
extractValue={extractValue}
|
||||
delimiter={delimiter}
|
||||
placeholder={optionsPlaceholder}
|
||||
data={filtedOptions}
|
||||
options={filtedOptions}
|
||||
highlightTxt={this.state.inputValue}
|
||||
multiple={multiple}
|
||||
initiallyOpen={initiallyOpen}
|
||||
|
@ -433,7 +433,7 @@ export default class TreeSelectControl extends React.Component<
|
|||
foldedField="collapsed"
|
||||
hideRoot
|
||||
value={value || ''}
|
||||
nameField="label"
|
||||
labelField="label"
|
||||
maxLength={maxLength}
|
||||
minLength={minLength}
|
||||
/>
|
||||
|
|
|
@ -691,6 +691,11 @@ export function mapTree<T extends TreeItem>(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历树
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function eachTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number) => any,
|
||||
|
@ -705,15 +710,19 @@ export function eachTree<T extends TreeItem>(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在树中查找节点。
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function findTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number) => any,
|
||||
level: number = 1
|
||||
iterator: (item: T, key: number, level: number, paths: Array<T>) => any
|
||||
): T | null {
|
||||
let result: T | null = null;
|
||||
|
||||
everyTree(tree, (item, key, level) => {
|
||||
if (iterator(item, key, level)) {
|
||||
everyTree(tree, (item, key, level, paths) => {
|
||||
if (iterator(item, key, level, paths)) {
|
||||
result = item;
|
||||
return false;
|
||||
}
|
||||
|
@ -723,6 +732,46 @@ export function findTree<T extends TreeItem>(
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在树中查找节点, 返回下标数组。
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function findTreeIndex<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number, paths: Array<T>) => any
|
||||
): Array<number> | undefined {
|
||||
let idx: Array<number> = [];
|
||||
|
||||
findTree(tree, (item, index, level, paths) => {
|
||||
if (iterator(item, index, level, paths)) {
|
||||
idx = [index];
|
||||
|
||||
paths = paths.concat();
|
||||
paths.unshift({
|
||||
children: tree
|
||||
} as any);
|
||||
|
||||
for (let i = paths.length - 1; i > 0; i--) {
|
||||
const prev = paths[i - 1];
|
||||
const current = paths[i];
|
||||
idx.unshift(prev.children!.indexOf(current));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return idx.length ? idx : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤树节点
|
||||
*
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function filterTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number) => boolean,
|
||||
|
@ -741,44 +790,111 @@ export function filterTree<T extends TreeItem>(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断树中每个节点是否满足某个条件。
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function everyTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number) => boolean,
|
||||
level: number = 1
|
||||
iterator: (item: T, key: number, level: number, paths: Array<T>) => boolean,
|
||||
level: number = 1,
|
||||
paths: Array<T> = []
|
||||
): boolean {
|
||||
return tree.every((item, index) => {
|
||||
const value: any = iterator(item, index, level);
|
||||
const value: any = iterator(item, index, level, paths);
|
||||
|
||||
if (value && item.children && item.children.splice) {
|
||||
return everyTree(item.children, iterator, level + 1);
|
||||
return everyTree(item.children, iterator, level + 1, paths.concat(item));
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断树中是否有某些节点满足某个条件。
|
||||
* @param tree
|
||||
* @param iterator
|
||||
*/
|
||||
export function someTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
iterator: (item: T, key: number, level: number) => boolean,
|
||||
level: number = 1
|
||||
iterator: (item: T, key: number, level: number, paths: Array<T>) => boolean
|
||||
): boolean {
|
||||
return tree.some((item, index) => {
|
||||
const value: any = iterator(item, index, level);
|
||||
|
||||
if (!value && item.children && item.children.splice) {
|
||||
return someTree(item.children, iterator, level + 1);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
return !everyTree(tree, iterator);
|
||||
}
|
||||
|
||||
export function flattenTree<T extends TreeItem>(tree: Array<T>): Array<T> {
|
||||
let flattened: Array<T> = [];
|
||||
eachTree(tree, item => flattened.push(item));
|
||||
/**
|
||||
* 将树打平变成一维数组,可以传入第二个参数实现打平节点中的其他属性。
|
||||
*
|
||||
* 比如:
|
||||
*
|
||||
* flattenTree([
|
||||
* {
|
||||
* id: 1,
|
||||
* children: [
|
||||
* { id: 2 },
|
||||
* { id: 3 },
|
||||
* ]
|
||||
* }
|
||||
* ], item => item.id); // 输出位 [1, 2, 3]
|
||||
*
|
||||
* @param tree
|
||||
* @param mapper
|
||||
*/
|
||||
export function flattenTree<T extends TreeItem>(tree: Array<T>): Array<T>;
|
||||
export function flattenTree<T extends TreeItem, U>(
|
||||
tree: Array<T>,
|
||||
mapper: (value: T, index: number) => U
|
||||
): Array<U>;
|
||||
export function flattenTree<T extends TreeItem, U>(
|
||||
tree: Array<T>,
|
||||
mapper?: (value: T, index: number) => U
|
||||
): Array<U> {
|
||||
let flattened: Array<any> = [];
|
||||
eachTree(tree, (item, index) =>
|
||||
flattened.push(mapper ? mapper(item, index) : item)
|
||||
);
|
||||
return flattened;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作树,遵循 imutable, 每次返回一个新的树。
|
||||
* 类似数组的 splice 不同的地方这个方法不修改原始数据,
|
||||
* 同时第二个参数不是下标,而是下标数组,分别代表每一层的下标。
|
||||
*
|
||||
* 至于如何获取下标数组,请查看 findTreeIndex
|
||||
*
|
||||
* @param tree
|
||||
* @param idx
|
||||
* @param deleteCount
|
||||
* @param ...items
|
||||
*/
|
||||
export function spliceTree<T extends TreeItem>(
|
||||
tree: Array<T>,
|
||||
idx: Array<number> | number,
|
||||
deleteCount: number = 0,
|
||||
...items: Array<T>
|
||||
): Array<T> {
|
||||
const list = tree.concat();
|
||||
if (typeof idx === 'number') {
|
||||
list.splice(idx, deleteCount, ...items);
|
||||
} else if (Array.isArray(idx) && idx.length) {
|
||||
const lastIdx = idx.pop()!;
|
||||
let host = idx.reduce((list: Array<T>, idx) => {
|
||||
const child = {
|
||||
...list[idx],
|
||||
children: list[idx].children ? list[idx].children!.concat() : []
|
||||
};
|
||||
list[idx] = child;
|
||||
return child.children;
|
||||
}, list);
|
||||
host.splice(lastIdx, deleteCount, ...items);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
export function ucFirst(str?: string) {
|
||||
return str ? str.substring(0, 1).toUpperCase() + str.substring(1) : '';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue