mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:39:05 +08:00
feat:调整列宽问题修复;新增sticky属性;列搜索;单元格支持编辑等
This commit is contained in:
parent
422376dec7
commit
34f73bf4ca
File diff suppressed because it is too large
Load Diff
@ -775,14 +775,13 @@ export const components = [
|
||||
import('../../docs/zh-CN/components/table.md').then(wrapDoc)
|
||||
)
|
||||
},
|
||||
{
|
||||
label: 'Table v2 表格',
|
||||
path: '/zh-CN/components/table-v2',
|
||||
getComponent: () =>
|
||||
import('../../docs/zh-CN/components/table-v2.md').then(
|
||||
makeMarkdownRenderer
|
||||
)
|
||||
},
|
||||
// {
|
||||
// label: 'Table v2 表格',
|
||||
// path: '/zh-CN/components/table-v2',
|
||||
// component: React.lazy(() =>
|
||||
// import('../../docs/zh-CN/components/table-v2.md').then(wrapDoc)
|
||||
// )
|
||||
// },
|
||||
{
|
||||
label: 'Table View 表格视图',
|
||||
path: '/zh-CN/components/table-view',
|
||||
|
@ -1043,6 +1043,7 @@ body.dark {
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
// overflow: hidden;
|
||||
width: calc(100% - 350px);
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
|
@ -1309,22 +1309,43 @@
|
||||
--Table-searchableForm-borderRadius: #{px2rem(4px)};
|
||||
|
||||
--TableCell--edge-paddingX: var(--gap-md);
|
||||
--TableCell--edge-paddingX-default: var(--gap-base);
|
||||
--TableCell-filterBtn--onActive-color: var(--primary);
|
||||
--TableCell-filterBtn-width: #{px2rem(16px)};
|
||||
--TableCell-filterPopOver-dropDownItem-height: #{px2rem(34px)};
|
||||
--TableCell-filterPopOver-dropDownItem-padding: 0 #{px2rem(12px)};
|
||||
--TableCell-line-height-large: #{px2rem(40px)};
|
||||
--TableCell-line-height-middle: #{px2rem(30px)};
|
||||
--TableCell-height: #{px2rem(40px)};
|
||||
--TableCell-height-default: #{px2rem(41px)};
|
||||
--TableCell-height-large: #{px2rem(61px)};
|
||||
--TableCell-height-small: #{px2rem(33px)};
|
||||
--TableCell-paddingX: var(--gap-sm);
|
||||
--TableCell-paddingX-large: var(--gap-base);
|
||||
--TableCell-paddingX-small: var(--gap-xs);
|
||||
--TableCell-paddingY: calc(
|
||||
(var(--TableCell-height) - var(--Table-fontSize) * var(--Table-lineHeight)) /
|
||||
2
|
||||
);
|
||||
--TableCell-paddingY-default: calc(
|
||||
(var(--TableCell-height-default) - var(--Table-fontSize) * var(--Table-lineHeight)) /
|
||||
2
|
||||
);
|
||||
--TableCell-paddingY-large: calc(
|
||||
(var(--TableCell-height-large) - var(--Table-fontSize) * var(--Table-lineHeight)) /
|
||||
2
|
||||
);
|
||||
--TableCell-paddingY-small: calc(
|
||||
(var(--TableCell-height-small) - var(--Table-fontSize) * var(--Table-lineHeight)) /
|
||||
2
|
||||
);
|
||||
--TableCell-searchBtn--onActive-color: var(--primary);
|
||||
--TableCell-searchBtn-width: #{px2rem(16px)};
|
||||
--TableCell-sortBtn--default-onActive-opacity: 1;
|
||||
--TableCell-sortBtn--default-opacity: 0;
|
||||
--TableCell-sortBtn--onActive-color: var(--primary);
|
||||
--TableCell-sortBtn-width: #{px2rem(8px)};
|
||||
--TableCell-icon-gap: var(--gap-sm);
|
||||
|
||||
--Table-fixedLeftLast-boxShadow: inset 10px 0 8px -8px #00000026;
|
||||
--Table-fixedRightFirst-boxShadow: inset -10px 0 8px -8px #00000026;
|
||||
|
912
scss/components/_table-v2.scss
Normal file
912
scss/components/_table-v2.scss
Normal file
@ -0,0 +1,912 @@
|
||||
.#{$ns}Table-v2 {
|
||||
position: relative;
|
||||
|
||||
border-radius: var(--Table-borderRadius);
|
||||
margin-bottom: var(--gap-md);
|
||||
|
||||
&.#{$ns}Table-bordered {
|
||||
border-width: var(--Table-borderWidth) var(--Table-borderWidth) 0 var(--Table-borderWidth);
|
||||
border-style: solid;
|
||||
border-color: var(--Table-borderColor);
|
||||
border-collapse: inherit;
|
||||
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th,
|
||||
> tbody > tr > td,
|
||||
> tfoot > tr > td {
|
||||
border-right: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-footer {
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
|
||||
.#{$ns}Table-title {
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-large {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr {
|
||||
> th {
|
||||
padding: var(--TableCell-paddingY-large) var(--TableCell-paddingX-large);
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr {
|
||||
> td,
|
||||
> th {
|
||||
padding: var(--TableCell-paddingY-large) var(--TableCell-paddingX-large);
|
||||
}
|
||||
}
|
||||
|
||||
> tfoot > tr {
|
||||
> td {
|
||||
padding: var(--TableCell-paddingY-large) var(--TableCell-paddingX-large);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-filterBtn {
|
||||
right: calc(
|
||||
var(--TableCell-paddingX-large) - var(--TableCell-filterBtn-width) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-small {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr {
|
||||
> th {
|
||||
padding: var(--TableCell-paddingY-small) var(--TableCell-paddingX-small);
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr {
|
||||
> td,
|
||||
> th {
|
||||
padding: var(--TableCell-paddingY-small) var(--TableCell-paddingX-small);
|
||||
}
|
||||
}
|
||||
|
||||
> tfoot > tr {
|
||||
> td {
|
||||
padding: var(--TableCell-paddingY-small) var(--TableCell-paddingX-small);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-filterBtn {
|
||||
right: calc(
|
||||
var(--TableCell-paddingX-small) - var(--TableCell-filterBtn-width) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-title,
|
||||
.#{$ns}Table-footer {
|
||||
background: var(--Table-heading-bg);
|
||||
padding: calc(
|
||||
(
|
||||
var(--Table-heading-height) - var(--Table-fontSize) *
|
||||
var(--lineHeightBase)
|
||||
) / 2
|
||||
)
|
||||
var(--gap-sm);
|
||||
}
|
||||
|
||||
.#{$ns}Table-header {
|
||||
padding: var(--Table-toolbar-marginY) var(--Table-toolbar-marginX);
|
||||
|
||||
&.#{$ns}Table-sticky-holder {
|
||||
position: sticky;
|
||||
z-index: 3;
|
||||
background: var(--Table-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-toolbar {
|
||||
@include clearfix();
|
||||
display: flex;
|
||||
margin: 0 var(--Table-toolbar-marginX) var(--Table-toolbar-marginY);
|
||||
flex-wrap: wrap;
|
||||
|
||||
.#{$ns}DropDown {
|
||||
&-menuItem {
|
||||
height: auto;
|
||||
|
||||
.#{$ns}Checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-header + .#{$ns}Table-toolbar {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.#{$ns}Table-content {
|
||||
min-height: 0.01%;
|
||||
overflow-x: auto;
|
||||
transform: translateZ(0);
|
||||
|
||||
th {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-table {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
margin-bottom: 0;
|
||||
font-size: var(--Table-fontSize);
|
||||
color: var(--Table-color);
|
||||
background: var(--Table-bg);
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
border: none;
|
||||
|
||||
& th,
|
||||
& td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
& th.text-center,
|
||||
& td.text-center,
|
||||
& th[colspan],
|
||||
& td[colspan] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
& th.text-right,
|
||||
& td.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
> thead > tr {
|
||||
> th {
|
||||
background: var(--Table-thead-bg);
|
||||
padding: var(--TableCell-paddingY-default) var(--TableCell-paddingX);
|
||||
|
||||
&:first-child {
|
||||
padding-left: var(--TableCell--edge-paddingX-default);
|
||||
}
|
||||
|
||||
&.#{$ns}Table-cell-last {
|
||||
padding-right: var(--TableCell--edge-paddingX-default);
|
||||
}
|
||||
|
||||
&:not(.#{$ns}Table-cell-last) {
|
||||
border-right: var(--Table-thead-borderWidth) solid
|
||||
var(--Table-thead-borderColor);
|
||||
}
|
||||
|
||||
&.#{$ns}Table-row-expand-icon-cell {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
font-size: var(--Table-thead-fontSize);
|
||||
color: var(--Table-thead-color);
|
||||
font-weight: var(--fontWeightNormal);
|
||||
white-space: nowrap;
|
||||
|
||||
.#{$ns}Remark {
|
||||
margin-left: var(--gap-xs);
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.#{$ns}Table-head-cell-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr:not(:last-child) {
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
|
||||
> tbody > tr {
|
||||
position: relative;
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
|
||||
&.#{$ns}Table-summary-row {
|
||||
> td {
|
||||
background: var(--Table-thead-bg);
|
||||
}
|
||||
}
|
||||
|
||||
> th {
|
||||
background: var(--Table-thead-bg);
|
||||
color: var(--Table-thead-color);
|
||||
font-weight: var(--fontWeightNormal);
|
||||
white-space: nowrap;
|
||||
border-right: var(--Table-thead-borderWidth) solid
|
||||
var(--Table-thead-borderColor);
|
||||
}
|
||||
|
||||
> td,
|
||||
> th {
|
||||
padding: var(--TableCell-paddingY-default) var(--TableCell-paddingX);
|
||||
|
||||
&:first-child {
|
||||
padding-left: var(--TableCell--edge-paddingX-default);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: var(--TableCell--edge-paddingX-default);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-cell-wrapper-prefix {
|
||||
display: flex;
|
||||
|
||||
.#{$ns}Table-expandBtn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-cell-height-large {
|
||||
height: var(--TableCell-line-height-large);
|
||||
line-height: var(--TableCell-line-height-large);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.#{$ns}Table-cell-height-middle {
|
||||
height: var(--TableCell-line-height-middle);
|
||||
line-height: var(--TableCell-line-height-middle);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@if var(--Table-strip-bg) !=transparent {
|
||||
background: transparent;
|
||||
|
||||
&.#{$ns}Table-tr--odd {
|
||||
background: var(--Table-strip-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-tr--hasItemAction:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.is-hovered {
|
||||
background: var(--Table-onHover-bg);
|
||||
border-color: var(--Table-onHover-borderColor);
|
||||
color: var(--Table-onHover-color);
|
||||
|
||||
& + tr {
|
||||
border-color: var(--Table-onHover-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-checked {
|
||||
background: var(--Table-onChecked-bg);
|
||||
border-color: var(--Table-onChecked-borderColor);
|
||||
color: var(--Table-onChecked-color);
|
||||
|
||||
& + tr {
|
||||
border-color: var(--Table-onChecked-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-moved,
|
||||
&.is-modified {
|
||||
background: var(--Table-onModified-bg);
|
||||
border-color: var(--Table-onModified-borderColor);
|
||||
color: var(--Table-onModified-color);
|
||||
|
||||
& + tr {
|
||||
border-color: var(--Table-onModified-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-summary {
|
||||
background: var(--Table-thead-bg);
|
||||
color: var(--Table-thead-color);
|
||||
font-weight: var(--fontWeightNormal);
|
||||
}
|
||||
|
||||
&.bg-light {
|
||||
@include color-variant($light, 2%, 3%, 3%, 5%);
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
&.bg-dark {
|
||||
@include color-variant($dark, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($dark);
|
||||
}
|
||||
|
||||
&.bg-black {
|
||||
@include color-variant($black, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($black);
|
||||
}
|
||||
|
||||
&.bg-primary {
|
||||
@include color-variant($primary, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($primary);
|
||||
}
|
||||
|
||||
&.bg-success {
|
||||
@include color-variant($success, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($success);
|
||||
}
|
||||
|
||||
&.bg-info {
|
||||
@include color-variant($info, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($info);
|
||||
}
|
||||
|
||||
&.bg-warning {
|
||||
@include color-variant($warning, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($warning);
|
||||
}
|
||||
|
||||
&.bg-danger {
|
||||
@include color-variant($danger, 5%, 10%, 5%, 10%);
|
||||
@include font-variant($danger);
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
opacity: var(--Table-onDragging-opacity);
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 2 through 10 {
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-expanded {
|
||||
.#{$ns}Table-expandCell:before {
|
||||
right: px2rem(7px) + px2rem(-18px) * ($i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th {
|
||||
.#{$ns}Table-expandBtn {
|
||||
position: relative;
|
||||
right: -(px2rem(18px)) * ($i - 1);
|
||||
}
|
||||
|
||||
.#{$ns}Table-expandCell + td {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: px2rem(1px);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: px2rem(-8px) + px2rem(18px) * ($i - 2);
|
||||
height: auto;
|
||||
background: var(--Table-tree-borderColor);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: px2rem(1px);
|
||||
top: 50%;
|
||||
left: px2rem(-8px) + px2rem(18px) * ($i - 2);
|
||||
width: px2rem(10px);
|
||||
background: var(--Table-tree-borderColor);
|
||||
}
|
||||
|
||||
padding-left: px2rem(18px) * $i - px2rem(18px);
|
||||
}
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-expandable {
|
||||
.#{$ns}Table-expandCell + td {
|
||||
padding-left: px2rem(18px) * ($i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
tr.#{$ns}Table-tr--#{$i}th.is-last:not(.is-expanded) {
|
||||
.#{$ns}Table-expandCell + td {
|
||||
&::before {
|
||||
height: 50%;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-checkCell,
|
||||
> tbody > tr > td.#{$ns}Table-checkCell {
|
||||
border-right: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
.#{$ns}Checkbox {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-expandCell,
|
||||
> tbody > tr > td.#{$ns}Table-expandCell {
|
||||
border-right: 0;
|
||||
width: px2rem(1px);
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-dragCell,
|
||||
> tbody > tr > td.#{$ns}Table-dragCell {
|
||||
border-right: 0;
|
||||
width: px2rem(1px);
|
||||
padding-right: 0;
|
||||
cursor: move;
|
||||
> svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-expandCell {
|
||||
position: relative;
|
||||
|
||||
@for $i from 1 through 7 {
|
||||
.#{$ns}Table-divider-#{$i} {
|
||||
position: absolute;
|
||||
width: px2rem(1px);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
background: var(--Table-tree-borderColor);
|
||||
right: px2rem(7px) + px2rem(-18px) * ($i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr.is-expanded > td.#{$ns}Table-expandCell {
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: px2rem(1px);
|
||||
top: 50%;
|
||||
bottom: 0;
|
||||
right: px2rem(7px);
|
||||
height: auto;
|
||||
background: var(--Table-tree-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}TableCell--sortable {
|
||||
padding-right: calc(
|
||||
var(--TableCell-paddingX) + var(--TableCell-sortBtn-width)
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}TableCell--searchable {
|
||||
padding-right: calc(
|
||||
var(--TableCell-paddingX) + var(--TableCell-searchBtn-width)
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}TableCell--filterable {
|
||||
padding-right: calc(
|
||||
var(--TableCell-paddingX) + var(--TableCell-filterBtn-width)
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> tbody > tr.#{$ns}Table-row-disabled {
|
||||
background: var(--TableRow-onDisabled-bg);
|
||||
color: var(--TableRow-onDisabled-color);
|
||||
}
|
||||
|
||||
> tbody > tr:not(.#{$ns}Table-row-disabled) > td.#{$ns}Table-cell-row-hover {
|
||||
background: var(--Table-onHover-bg);
|
||||
border-color: var(--Table-onHover-borderColor);
|
||||
color: var(--Table-onHover-color);
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-left-last,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-left-last,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-left-last {
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: -1px;
|
||||
width: 30px;
|
||||
transform: translate(100%);
|
||||
transition: box-shadow .3s;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-right-first,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-right-last {
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 30px;
|
||||
transform: translate(-100%);
|
||||
transition: box-shadow .3s;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-cell-expand-icon-cell {
|
||||
text-align: center;
|
||||
|
||||
.#{$ns}Table-row-indent {
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr.#{$ns}Table-expanded-row > td {
|
||||
background: var(--Table-onHover-bg);
|
||||
}
|
||||
|
||||
> tfoot > tr {
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
|
||||
> td {
|
||||
padding: var(--TableCell-paddingY-default) var(--TableCell-paddingX);
|
||||
background: var(--Table-thead-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-container {
|
||||
.#{$ns}Table-header {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-ping-left {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-left-last,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-left-last,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-left-last {
|
||||
&:after {
|
||||
box-shadow: var(--Table-fixedLeftLast-boxShadow);
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr:not(.#{$ns}Table-row-disabled) {
|
||||
> td.#{$ns}Table-cell-fix-left {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
> td.#{$ns}Table-cell-fix-left:not(.#{$ns}Table-cell-row-hover) {
|
||||
background: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
> tfoot > tr > td:not(:last-child) {
|
||||
&.#{$ns}Table-cell-fix-left-last {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th:not(:last-child):not(:first-child) {
|
||||
&.#{$ns}Table-cell-fix-left-last {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-ping-right {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-right-first,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-right-first {
|
||||
&:after {
|
||||
box-shadow: var(--Table-fixedRightFirst-boxShadow);
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr:not(.#{$ns}Table-row-disabled) {
|
||||
> td.#{$ns}Table-cell-fix-right {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
> td.#{$ns}Table-cell-fix-right:not(.#{$ns}Table-cell-row-hover) {
|
||||
background: #FFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.#{$ns}Table-bordered) {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first-prev {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
> thead > tr > th:not(:last-child) {
|
||||
&.#{$ns}Table-cell-fix-right-first {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-resizable {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th {
|
||||
position: relative;
|
||||
|
||||
.#{$ns}Table-thead-resizable {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-loading {
|
||||
padding: var(--Table-loading-padding);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-sortBtn {
|
||||
cursor: pointer;
|
||||
width: var(--TableCell-sortBtn-width);
|
||||
height: var(--gap-md);
|
||||
position: static;
|
||||
display: inline-block;
|
||||
transform: none;
|
||||
color: var(--icon-color);
|
||||
margin-left: var(--TableCell-icon-gap);
|
||||
|
||||
&:hover {
|
||||
color: var(--icon-onHover-color);
|
||||
}
|
||||
|
||||
&--up > svg,
|
||||
&--down > svg,
|
||||
&--default > svg {
|
||||
color: inherit;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
&--up,
|
||||
&--down,
|
||||
&--default {
|
||||
display: none;
|
||||
z-index: 2;
|
||||
font-style: normal;
|
||||
|
||||
&.is-active {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
&--default {
|
||||
&.is-active {
|
||||
color: var(--text--muted-color);
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
&--up,
|
||||
&--down {
|
||||
&.is-active {
|
||||
color: var(--TableCell-sortBtn--onActive-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-searchBtn {
|
||||
cursor: pointer;
|
||||
position: static;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text--muted-color);
|
||||
margin-left: var(--TableCell-icon-gap);
|
||||
|
||||
svg.icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
&.is-active {
|
||||
color: var(--TableCell-searchBtn--onActive-color);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-searchPopOver {
|
||||
border: none;
|
||||
min-width: px2rem(320px);
|
||||
max-width: px2rem(640px);
|
||||
|
||||
.#{$ns}Panel {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-filterBtn {
|
||||
cursor: pointer;
|
||||
width: var(--TableCell-filterBtn-width);
|
||||
position: static;
|
||||
display: inline-block;
|
||||
transform: none;
|
||||
color: var(--text--muted-color);
|
||||
margin-left: var(--TableCell-icon-gap);
|
||||
|
||||
svg.icon {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: var(--TableCell-filterBtn--onActive-color);
|
||||
}
|
||||
|
||||
.#{$ns}Remark {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-filterPopOver {
|
||||
border: none;
|
||||
width: px2rem(160px);
|
||||
|
||||
.#{$ns}DropDown-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.#{$ns}DropDown-divider {
|
||||
height: var(--TableCell-filterPopOver-dropDownItem-height);
|
||||
line-height: var(--TableCell-filterPopOver-dropDownItem-height);
|
||||
padding: var(--TableCell-filterPopOver-dropDownItem-padding);
|
||||
background: var(--white);
|
||||
margin: 0;
|
||||
|
||||
&:hover {
|
||||
background: var(--light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
background: var(--light);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.#{$ns}Checkbox {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}DropDown-multiple-menu {
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--Table-borderColor);
|
||||
|
||||
.#{$ns}Button {
|
||||
margin: 0 5px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-selectionBtn {
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
|
||||
svg.icon {
|
||||
transform: rotate(270deg);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TableCell-selectionPopOver {
|
||||
.#{$ns}DropDown-menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-expandBtn {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--Table-expandBtn-color);
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: px2rem(14px);
|
||||
line-height: 1;
|
||||
height: 16px;
|
||||
|
||||
> svg {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: transform ease-in-out var(--animation-duration),
|
||||
top ease-in-out var(--animation-duration);
|
||||
position: relative;
|
||||
transform-origin: 50% 50%;
|
||||
width: px2rem(10px);
|
||||
height: px2rem(10px);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.is-active > svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-table > tbody > tr:hover .#{$ns}Table-dragBtn,
|
||||
.#{$ns}Table-table > tbody > tr.is-dragging .#{$ns}Table-dragBtn,
|
||||
.#{$ns}Table-table > tbody > tr.is-drop-allowed .#{$ns}Table-dragBtn {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.fake-hide {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.#{$ns}Table-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}InputTable-toolbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.#{$ns}InputTable-pager {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.#{$ns}OperationField {
|
||||
margin: px2rem(-3px);
|
||||
|
||||
> .#{$ns}Button,
|
||||
> .#{$ns}Button--disabled-wrap > .#{$ns}Button {
|
||||
margin: px2rem(3px);
|
||||
}
|
||||
|
||||
> .#{$ns}Button--disabled-wrap > .#{$ns}Button--link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .#{$ns}Button--link {
|
||||
padding: 0;
|
||||
margin-right: px2rem(10px);
|
||||
}
|
||||
}
|
@ -8,34 +8,6 @@
|
||||
margin-bottom: var(--gap-sm);
|
||||
}
|
||||
|
||||
&-bordered {
|
||||
border: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
border-collapse: inherit;
|
||||
|
||||
.#{$ns}Table-container {
|
||||
border-top: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th,
|
||||
> tbody > tr > td,
|
||||
> tfoot > tr > td {
|
||||
border-right: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-footer {
|
||||
border-top: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
.#{$ns}Table-title {
|
||||
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&-fixedLeft,
|
||||
&-fixedRight {
|
||||
position: absolute;
|
||||
@ -127,9 +99,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-heading,
|
||||
&-title,
|
||||
&-footer {
|
||||
&-heading {
|
||||
background: var(--Table-heading-bg);
|
||||
padding: calc(
|
||||
(
|
||||
@ -261,6 +231,7 @@
|
||||
background: var(--Table-bg);
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
border: var(--Table-borderWidth) solid var(--Table-borderColor);
|
||||
|
||||
& th,
|
||||
& td {
|
||||
@ -632,124 +603,6 @@
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-left,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-right,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-left,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-right {
|
||||
background: #FFF;
|
||||
}
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-cell-row-hover {
|
||||
background: var(--Table-onHover-bg);
|
||||
border-color: var(--Table-onHover-borderColor);
|
||||
color: var(--Table-onHover-color);
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-left-last,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-left-last,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-left-last {
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: -1px;
|
||||
width: 30px;
|
||||
transform: translate(100%);
|
||||
transition: box-shadow .3s;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-right-first,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-right-last {
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 30px;
|
||||
transform: translate(-100%);
|
||||
transition: box-shadow .3s;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr > td.#{$ns}Table-cell-expand-icon-cell {
|
||||
text-align: center;
|
||||
|
||||
.fa-minus-square,
|
||||
.fa-plus-square {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> tbody > tr.#{$ns}Table-expanded-row > td {
|
||||
background: var(--Table-onHover-bg);
|
||||
}
|
||||
|
||||
> tfoot > tr > td {
|
||||
padding: var(--TableCell-paddingY) var(--TableCell-paddingX);
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
.#{$ns}Table-header {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-ping-left {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-left-last,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-left-last,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-left-last {
|
||||
&:after {
|
||||
box-shadow: var(--Table-fixedLeftLast-boxShadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-ping-right {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first,
|
||||
> tbody > tr > td.#{$ns}Table-cell-fix-right-first,
|
||||
> tfoot > tr > td.#{$ns}Table-cell-fix-right-first {
|
||||
&:after {
|
||||
box-shadow: var(--Table-fixedRightFirst-boxShadow);
|
||||
}
|
||||
}
|
||||
|
||||
> thead > tr > th.#{$ns}Table-cell-fix-right-first {
|
||||
border-right: var(--Table-thead-borderWidth) solid var(--Table-thead-borderColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Table-resizable {
|
||||
.#{$ns}Table-table {
|
||||
> thead > tr > th {
|
||||
position: relative;
|
||||
|
||||
.#{$ns}Table-thead-resizable {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
cursor: col-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Table-loading {
|
||||
padding: var(--Table-loading-padding);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&Cell-sortBtn {
|
||||
@ -805,9 +658,6 @@
|
||||
color: var(--TableCell-sortBtn--onActive-color);
|
||||
}
|
||||
}
|
||||
&:not(:last-child) {
|
||||
right: calc(var(--TableCell-paddingX) - var(--TableCell-sortBtn-width) / 2 + 15px);
|
||||
}
|
||||
}
|
||||
|
||||
&Cell-searchBtn {
|
||||
@ -903,20 +753,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}DropDown-multiple-menu {
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--Table-borderColor);
|
||||
|
||||
.#{$ns}Button {
|
||||
margin: 0 5px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-itemActions-wrap {
|
||||
|
@ -49,6 +49,7 @@
|
||||
@import '../components/wizard';
|
||||
@import '../components/crud';
|
||||
@import '../components/table';
|
||||
@import '../components/table-v2';
|
||||
@import '../components/column-toggler';
|
||||
@import '../components/list';
|
||||
@import '../components/cards';
|
||||
|
@ -369,13 +369,13 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
||||
--Table-color: #333;
|
||||
--Table-thead-color: #333;
|
||||
--Table-lineHeight: 20 / 12;
|
||||
--Table-borderColor: #f5f5f5;
|
||||
--Table-borderColor: #{$G8};
|
||||
--Table-tree-borderColor: #{darken(#f5f5f5, 10%)};
|
||||
--Table-thead-bg: #f5f5f5;
|
||||
--Table-thead-bg: #{$G10};
|
||||
--Table-thead-borderColor: #fff;
|
||||
--Table-thead-iconColor: #999;
|
||||
--Table-strip-bg: transparent;
|
||||
--Table-onHover-bg: #f5f5f5;
|
||||
--Table-onHover-bg: #{$B1};
|
||||
--Table-onHover-bg-rgb: 245, 251, 255;
|
||||
--Table-onHover-borderColor: #eceff8;
|
||||
--Table-onChecked-bg: #f0faff;
|
||||
@ -391,6 +391,9 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
||||
--TableCell-filterPopOver-dropDownItem-height: #{px2rem(30px)};
|
||||
--TableCell-filterPopOver-dropDownItem-padding: 0 #{px2rem(10px)};
|
||||
|
||||
--TableRow-onDisabled-bg: #{$G10};
|
||||
--TableRow-onDisabled-color: #{$G6};
|
||||
|
||||
// listControl
|
||||
--ListControl-item-borderWidth: #{px2rem(1px)};
|
||||
--ListControl-item-borderRadius: #{$R3};
|
||||
|
@ -118,6 +118,7 @@ import {FormControlSchema} from './renderers/Form/Control';
|
||||
import {TransferPickerControlSchema} from './renderers/Form/TransferPicker';
|
||||
import {TabsTransferPickerControlSchema} from './renderers/Form/TabsTransferPicker';
|
||||
import {JSONSchemaEditorControlSchema} from './renderers/Form/JSONSchemaEditor';
|
||||
import {TableSchemaV2} from './renderers/Table-v2';
|
||||
|
||||
// 每加个类型,这补充一下。
|
||||
export type SchemaType =
|
||||
@ -392,6 +393,7 @@ export type SchemaObject =
|
||||
| StatusSchema
|
||||
| SpinnerSchema
|
||||
| TableSchema
|
||||
| TableSchemaV2
|
||||
| TabsSchema
|
||||
| TasksSchema
|
||||
| VBoxSchema
|
||||
|
@ -20,12 +20,16 @@ export interface Props extends ThemeProps, LocaleProps {
|
||||
children?: any;
|
||||
tagName?: string;
|
||||
style?: Object;
|
||||
column?: ColumnProps
|
||||
column?: ColumnProps;
|
||||
wrapperComponent: any;
|
||||
groupId?: string; // 表头分组随机生成的id
|
||||
depth?: number; // 表头分组
|
||||
}
|
||||
|
||||
export class BodyCell extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
fixed: '',
|
||||
wrapperComponent: 'td',
|
||||
rowSpan: null,
|
||||
colSpan: null
|
||||
};
|
||||
@ -38,28 +42,16 @@ export class BodyCell extends React.Component<Props> {
|
||||
key,
|
||||
children,
|
||||
className,
|
||||
tagName,
|
||||
style,
|
||||
column,
|
||||
style,
|
||||
groupId,
|
||||
depth,
|
||||
wrapperComponent: Component,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
|
||||
if (tagName === 'TH') {
|
||||
return (
|
||||
<th
|
||||
key={key || null}
|
||||
rowSpan={rowSpan && rowSpan > 1 ? rowSpan : null}
|
||||
colSpan={colSpan && colSpan > 1 ? colSpan : null}
|
||||
className={cx('Table-cell', className, {
|
||||
[cx(`Table-cell-fix-${fixed}`)] : fixed
|
||||
})}
|
||||
style={fixed ? {position: 'sticky', zIndex} : style}
|
||||
>{children}</th>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
<Component
|
||||
key={key || null}
|
||||
rowSpan={rowSpan && rowSpan > 1 ? rowSpan : null}
|
||||
colSpan={colSpan && colSpan > 1 ? colSpan : null}
|
||||
@ -67,8 +59,10 @@ export class BodyCell extends React.Component<Props> {
|
||||
[cx(`Table-cell-fix-${fixed}`)] : fixed,
|
||||
[`text-${column?.align}`] : column?.align
|
||||
})}
|
||||
style={fixed ? {position: 'sticky', zIndex} : {}}
|
||||
>{children}</td>
|
||||
style={fixed ? {position: 'sticky', zIndex, ...style} : {...style}}
|
||||
data-group-id={groupId || null}
|
||||
data-depth={depth || null}
|
||||
>{children}</Component>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
115
src/components/table/HeadCellDropDown.tsx
Normal file
115
src/components/table/HeadCellDropDown.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @file table/HeadCellDropDown
|
||||
* @author fex
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
|
||||
import {themeable, ThemeProps} from '../../theme';
|
||||
import {LocaleProps, localeable} from '../../locale';
|
||||
import Overlay from '../Overlay';
|
||||
import PopOver from '../PopOver';
|
||||
|
||||
export interface FilterPayload {
|
||||
closeDropdown?: boolean;
|
||||
}
|
||||
|
||||
export interface FilterDropdownProps {
|
||||
setSelectedKeys?: (keys: Array<string | number> | string) => void,
|
||||
selectedKeys?: Array<string | number> | string,
|
||||
confirm: (payload: FilterPayload) => void,
|
||||
clearFilters?: () => void
|
||||
}
|
||||
|
||||
export interface Props extends ThemeProps, LocaleProps {
|
||||
filterIcon: Function | React.ReactNode; // 图标方法 返回ReactNode
|
||||
className: string; // 图标样式
|
||||
layerClassName: string; // 展开层样式
|
||||
active: boolean; // 图标是否高亮
|
||||
popOverContainer?: () => Element | Text | null;
|
||||
filterDropdown: (payload: FilterDropdownProps) => JSX.Element | null ; // 菜单内容
|
||||
selectedKeys?: Array<string | number> | string;
|
||||
setSelectedKeys?: (keys: Array<string | number> | string) => void;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
isOpened: boolean;
|
||||
}
|
||||
|
||||
export class HeadCellDropDown extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isOpened: false
|
||||
}
|
||||
|
||||
this.openLayer = this.openLayer.bind(this);
|
||||
this.closeLayer = this.closeLayer.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isOpened} = this.state;
|
||||
const {
|
||||
popOverContainer,
|
||||
active,
|
||||
className,
|
||||
layerClassName,
|
||||
filterIcon,
|
||||
filterDropdown,
|
||||
classnames: cx,
|
||||
classPrefix: ns
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cx(
|
||||
className,
|
||||
active ? 'is-active' : ''
|
||||
)}
|
||||
>
|
||||
<span onClick={this.openLayer}>
|
||||
{filterIcon && typeof filterIcon === 'function'
|
||||
? filterIcon(active) : (filterIcon || null)}
|
||||
</span>
|
||||
{
|
||||
isOpened ? (
|
||||
<Overlay
|
||||
container={popOverContainer || (() => findDOMNode(this))}
|
||||
placement="left-bottom-left-top right-bottom-right-top"
|
||||
target={
|
||||
popOverContainer ? () => findDOMNode(this)!.parentNode : null
|
||||
}
|
||||
show
|
||||
>
|
||||
<PopOver
|
||||
classPrefix={ns}
|
||||
onHide={this.closeLayer}
|
||||
className={cx(layerClassName)}
|
||||
overlay
|
||||
>
|
||||
{filterDropdown && typeof filterDropdown === 'function'
|
||||
? filterDropdown({...this.props, confirm: (payload: FilterPayload) => {
|
||||
if (!(payload && payload.closeDropdown === false)) {
|
||||
this.closeLayer();
|
||||
}
|
||||
}}) : (filterDropdown || null)}
|
||||
</PopOver>
|
||||
</Overlay>)
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
openLayer() {
|
||||
this.setState({isOpened: true});
|
||||
}
|
||||
|
||||
closeLayer() {
|
||||
this.setState({isOpened: false});
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(localeable(HeadCellDropDown));
|
@ -9,19 +9,17 @@ import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {themeable, ThemeProps} from '../../theme';
|
||||
import {LocaleProps, localeable} from '../../locale';
|
||||
import Overlay from '../Overlay';
|
||||
import PopOver from '../PopOver';
|
||||
import HeadCellDropDown, {FilterDropdownProps, FilterPayload} from './HeadCellDropDown';
|
||||
import CheckBox from '../Checkbox';
|
||||
import Button from '../Button';
|
||||
import {Icon} from '../icons';
|
||||
|
||||
export interface Props extends ThemeProps, LocaleProps {
|
||||
column: any;
|
||||
popOverContainer?: () => Element | Text | null;
|
||||
onFilter?: Function;
|
||||
onQuery?: Function;
|
||||
filteredValue?: Array<string>;
|
||||
filterMultiple?: boolean;
|
||||
popOverContainer?: () => Element | Text | null;
|
||||
}
|
||||
|
||||
export interface OptionProps {
|
||||
@ -33,7 +31,6 @@ export interface OptionProps {
|
||||
|
||||
export interface State {
|
||||
options: Array<OptionProps>;
|
||||
isOpened: boolean;
|
||||
filteredValue: Array<string>;
|
||||
}
|
||||
|
||||
@ -48,17 +45,11 @@ export class HeadCellFilter extends React.Component<Props, State> {
|
||||
|
||||
this.state = {
|
||||
options: [],
|
||||
isOpened: false,
|
||||
filteredValue: props.filteredValue || []
|
||||
}
|
||||
|
||||
this.openLayer = this.openLayer.bind(this);
|
||||
this.closeLayer = this.closeLayer.bind(this);
|
||||
}
|
||||
|
||||
alterOptions(options: Array<any>) {
|
||||
const {column} = this.props;
|
||||
|
||||
options = options.map(option => ({
|
||||
...option,
|
||||
selected: this.state.filteredValue.indexOf(option.value) > -1
|
||||
@ -83,7 +74,7 @@ export class HeadCellFilter extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isOpened, options} = this.state;
|
||||
const {options} = this.state;
|
||||
const {
|
||||
column,
|
||||
popOverContainer,
|
||||
@ -91,121 +82,112 @@ export class HeadCellFilter extends React.Component<Props, State> {
|
||||
classPrefix: ns
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cx(
|
||||
`${ns}TableCell-filterBtn`,
|
||||
column.filtered || options && options.some((item: any) => item.selected) ? 'is-active' : ''
|
||||
)}
|
||||
>
|
||||
<span onClick={this.openLayer}>
|
||||
<Icon icon="column-filter" className="icon" />
|
||||
</span>
|
||||
{
|
||||
isOpened ? (
|
||||
<Overlay
|
||||
container={popOverContainer || (() => findDOMNode(this))}
|
||||
placement="left-bottom-left-top right-bottom-right-top"
|
||||
target={
|
||||
popOverContainer ? () => findDOMNode(this) : null
|
||||
}
|
||||
show
|
||||
>
|
||||
<PopOver
|
||||
classPrefix={ns}
|
||||
onHide={this.closeLayer}
|
||||
className={cx(
|
||||
`${ns}TableCell-filterPopOver`
|
||||
)}
|
||||
overlay
|
||||
>
|
||||
{options && options.length > 0 ? (
|
||||
<ul className={cx('DropDown-menu')}>
|
||||
{!column.filterMultiple
|
||||
? options.map((option: any, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={cx({
|
||||
'is-active': option.selected
|
||||
})}
|
||||
onClick={this.handleClick.bind(this, option.value)}
|
||||
>
|
||||
{option.text}
|
||||
</li>
|
||||
))
|
||||
: options.map((option: any, index) => (
|
||||
<li key={index}>
|
||||
<CheckBox
|
||||
classPrefix={ns}
|
||||
onChange={this.handleCheck.bind(this, option.value)}
|
||||
checked={option.selected}
|
||||
>
|
||||
{option.text}
|
||||
</CheckBox>
|
||||
</li>
|
||||
))}
|
||||
{column.filterMultiple ? (
|
||||
<li
|
||||
key="DropDown-multiple-menu"
|
||||
className={cx('DropDown-multiple-menu')}
|
||||
const filterProps = {
|
||||
filterDropdown: (payload: FilterDropdownProps) => {
|
||||
const {setSelectedKeys, selectedKeys, confirm, clearFilters} = payload
|
||||
return options && options.length > 0 ? (
|
||||
<ul className={cx('DropDown-menu')}>
|
||||
{!column.filterMultiple
|
||||
? options.map((option: any, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={cx({
|
||||
'is-active': option.selected
|
||||
})}
|
||||
onClick={() => this.handleClick(confirm, setSelectedKeys, [option.value])}
|
||||
>
|
||||
{option.text}
|
||||
</li>
|
||||
))
|
||||
: options.map((option: any, index) => (
|
||||
<li key={index}>
|
||||
<CheckBox
|
||||
classPrefix={ns}
|
||||
onChange={e =>
|
||||
this.handleCheck(confirm, setSelectedKeys, e
|
||||
? [option.value] : option.value)}
|
||||
checked={option.selected}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
level={'primary'}
|
||||
onClick={this.handleConfirmClick.bind(this)}
|
||||
>确定</Button>
|
||||
<Button
|
||||
size={'xs'}
|
||||
onClick={this.handleCancelClick.bind(this)}
|
||||
>取消</Button>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
) : null}
|
||||
</PopOver>
|
||||
</Overlay>)
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
{option.text}
|
||||
</CheckBox>
|
||||
</li>
|
||||
))}
|
||||
{column.filterMultiple ? (
|
||||
<li
|
||||
key="dropDown-multiple-menu"
|
||||
className={cx('DropDown-multiple-menu')}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
level={'primary'}
|
||||
onClick={() => this.handleConfirmClick(confirm)}
|
||||
>确定</Button>
|
||||
<Button
|
||||
size={'xs'}
|
||||
onClick={() => this.handleCancelClick(confirm, setSelectedKeys)}
|
||||
>取消</Button>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
) : null
|
||||
},
|
||||
setSelectedKeys: (keys: Array<string>) => this.setState({filteredValue: keys})
|
||||
};
|
||||
|
||||
return (
|
||||
<HeadCellDropDown
|
||||
className={`${ns}TableCell-filterBtn`}
|
||||
layerClassName={`${ns}TableCell-filterPopOver`}
|
||||
filterIcon={<Icon icon="column-filter" className="icon" />}
|
||||
active={column.filtered || options && options.some((item: any) => item.selected)}
|
||||
popOverContainer={popOverContainer ? popOverContainer : () => findDOMNode(this)}
|
||||
selectedKeys={this.state.filteredValue}
|
||||
{...filterProps}
|
||||
>
|
||||
</HeadCellDropDown>
|
||||
);
|
||||
}
|
||||
|
||||
openLayer() {
|
||||
this.setState({isOpened: true});
|
||||
handleClick(
|
||||
confirm: (payload?: FilterPayload) => void,
|
||||
setSelectedKeys?: (keys?: (string | Array<string | number>)) => void,
|
||||
selectedKeys?: Array<string>
|
||||
) {
|
||||
const {onFilter, column} = this.props;
|
||||
|
||||
setSelectedKeys && setSelectedKeys(selectedKeys);
|
||||
|
||||
onFilter && onFilter({[column.key] : selectedKeys});
|
||||
confirm();
|
||||
}
|
||||
|
||||
closeLayer() {
|
||||
this.setState({isOpened: false});
|
||||
}
|
||||
|
||||
handleClick(value: string) {
|
||||
const {onQuery, column} = this.props;
|
||||
|
||||
this.setState({filteredValue: [value]});
|
||||
|
||||
onQuery && onQuery({[column.key] : value});
|
||||
this.closeLayer();
|
||||
}
|
||||
|
||||
handleCheck(value: string) {
|
||||
handleCheck(
|
||||
confirm: (payload?: FilterPayload) => void,
|
||||
setSelectedKeys?: ((keys: (string | Array<string | number>)) => void | undefined),
|
||||
selectedKeys?: Array<string>
|
||||
) {
|
||||
const filteredValue = this.state.filteredValue;
|
||||
if (value) {
|
||||
this.setState({filteredValue: [...filteredValue, value]});
|
||||
} else {
|
||||
this.setState({filteredValue: filteredValue.filter(v => v !== value)});
|
||||
// 选中
|
||||
if (Array.isArray(selectedKeys)) {
|
||||
setSelectedKeys && setSelectedKeys([...filteredValue, ...selectedKeys]);
|
||||
} else { // 取消选中
|
||||
setSelectedKeys && setSelectedKeys(filteredValue.filter(v => v !== selectedKeys));
|
||||
}
|
||||
}
|
||||
|
||||
handleConfirmClick() {
|
||||
const {onQuery, column} = this.props;
|
||||
onQuery && onQuery({[column.key] : this.state.filteredValue});
|
||||
this.closeLayer();
|
||||
handleConfirmClick(confirm: (payload?: FilterPayload) => void) {
|
||||
const {onFilter, column} = this.props;
|
||||
onFilter && onFilter({[column.key] : this.state.filteredValue});
|
||||
confirm();
|
||||
}
|
||||
|
||||
handleCancelClick() {
|
||||
this.setState({filteredValue: []});
|
||||
this.closeLayer();
|
||||
handleCancelClick(
|
||||
confirm: (payload?: FilterPayload) => void,
|
||||
setSelectedKeys?: ((keys: (string | Array<string | number>)) => void | undefined)
|
||||
) {
|
||||
setSelectedKeys && setSelectedKeys([]);
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(localeable(HeadCellFilter));
|
||||
export default themeable(localeable(HeadCellFilter));
|
||||
|
86
src/components/table/HeadCellSelect.tsx
Normal file
86
src/components/table/HeadCellSelect.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file table/HeadCellSelect
|
||||
* @author fex
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
|
||||
import {themeable, ThemeProps} from '../../theme';
|
||||
import {LocaleProps, localeable} from '../../locale';
|
||||
import HeadCellDropDown, {FilterPayload} from './HeadCellDropDown';
|
||||
import {RowSelectionOptionProps} from './index';
|
||||
import {Icon} from '../icons';
|
||||
|
||||
export interface Props extends ThemeProps, LocaleProps {
|
||||
selections: Array<RowSelectionOptionProps>;
|
||||
keys: Array<string | number> | string;
|
||||
popOverContainer?: () => Element | Text | null;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
key: Array<string | number> | string;
|
||||
}
|
||||
|
||||
export class HeadCellSelect extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
selections: []
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
key: ''
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
selections,
|
||||
keys: allKeys,
|
||||
popOverContainer,
|
||||
classnames: cx,
|
||||
classPrefix: ns
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<HeadCellDropDown
|
||||
className={`${ns}TableCell-selectionBtn`}
|
||||
layerClassName={`${ns}TableCell-selectionPopOver`}
|
||||
filterIcon={<Icon icon="left-arrow" className="icon" />}
|
||||
active={false}
|
||||
popOverContainer={popOverContainer ? popOverContainer : () => findDOMNode(this)}
|
||||
filterDropdown={({setSelectedKeys, selectedKeys, confirm, clearFilters}) => {
|
||||
return <ul className={cx('DropDown-menu')}>
|
||||
{selections.map((item, index) => (
|
||||
<li
|
||||
key={index}
|
||||
onClick={() => {
|
||||
item.onSelect && item.onSelect(allKeys);
|
||||
this.handleClick(confirm, setSelectedKeys, item.key);
|
||||
}}>
|
||||
{item.text}
|
||||
</li>
|
||||
))}
|
||||
</ul> ;
|
||||
}}
|
||||
setSelectedKeys={keys => this.setState({key: keys})}
|
||||
selectedKeys={this.state.key}
|
||||
>
|
||||
</HeadCellDropDown>
|
||||
);
|
||||
}
|
||||
|
||||
handleClick(
|
||||
confirm: (payload?: FilterPayload) => void,
|
||||
setSelectedKeys?: (keys?: Array<string | number> | string) => void | undefined,
|
||||
selectedKeys?: Array<string> | string
|
||||
) {
|
||||
setSelectedKeys && setSelectedKeys(selectedKeys);
|
||||
|
||||
confirm();
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(localeable(HeadCellSelect));
|
File diff suppressed because it is too large
Load Diff
@ -322,14 +322,11 @@ export const HocQuickEdit =
|
||||
);
|
||||
}
|
||||
|
||||
openQuickEdit(e) {
|
||||
openQuickEdit() {
|
||||
currentOpened = this;
|
||||
this.setState({
|
||||
isOpened: true
|
||||
});
|
||||
// QuickEdit在table中使用时,如果table配置了checkOnItemClick,会同时触发行选中
|
||||
// 所以这里阻止冒泡一下
|
||||
e.stopPropagation && e.stopPropagation();
|
||||
}
|
||||
|
||||
closeQuickEdit() {
|
||||
|
244
src/renderers/Table-v2/HeadCellSearchDropdown.tsx
Normal file
244
src/renderers/Table-v2/HeadCellSearchDropdown.tsx
Normal file
@ -0,0 +1,244 @@
|
||||
import React from 'react';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
|
||||
import {RendererProps} from '../../factory';
|
||||
import {Action} from '../../types';
|
||||
import {Icon} from '../../components/icons';
|
||||
import {setVariable} from '../../utils/helper';
|
||||
import {ITableStore} from '../../store/table-v2';
|
||||
import HeadCellDropDown from '../../components/table/HeadCellDropDown';
|
||||
|
||||
export interface QuickSearchConfig {
|
||||
type?: string;
|
||||
controls?: any;
|
||||
tabs?: any;
|
||||
fieldSet?: any;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface HeadCellSearchProps extends RendererProps {
|
||||
name: string;
|
||||
searchable: boolean | QuickSearchConfig;
|
||||
classPrefix: string;
|
||||
onFilter?: (values: object) => void;
|
||||
onAction?: Function;
|
||||
store: ITableStore;
|
||||
}
|
||||
|
||||
export class HeadCellSearchDropDown extends React.Component<
|
||||
HeadCellSearchProps,
|
||||
any
|
||||
> {
|
||||
|
||||
formItems: Array<string> = [];
|
||||
constructor(props: HeadCellSearchProps) {
|
||||
super(props);
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleAction = this.handleAction.bind(this);
|
||||
}
|
||||
|
||||
buildSchema() {
|
||||
const {searchable, sortable, name, label, translate: __} = this.props;
|
||||
|
||||
let schema: any;
|
||||
|
||||
if (searchable === true) {
|
||||
schema = {
|
||||
title: '',
|
||||
controls: [
|
||||
{
|
||||
type: 'text',
|
||||
name,
|
||||
placeholder: label,
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
};
|
||||
} else if (searchable) {
|
||||
if (searchable.controls || searchable.tabs || searchable.fieldSet) {
|
||||
schema = {
|
||||
title: '',
|
||||
...searchable,
|
||||
controls: Array.isArray(searchable.controls)
|
||||
? searchable.controls.concat()
|
||||
: undefined
|
||||
};
|
||||
} else {
|
||||
schema = {
|
||||
title: '',
|
||||
className: searchable.formClassName,
|
||||
controls: [
|
||||
{
|
||||
type: searchable.type || 'text',
|
||||
name: searchable.name || name,
|
||||
placeholder: label,
|
||||
...searchable
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (schema && schema.controls && sortable) {
|
||||
schema.controls.unshift(
|
||||
{
|
||||
type: 'hidden',
|
||||
name: 'orderBy',
|
||||
value: name
|
||||
},
|
||||
{
|
||||
type: 'button-group',
|
||||
name: 'order',
|
||||
label: __('sort'),
|
||||
options: [
|
||||
{
|
||||
label: __('asc'),
|
||||
value: 'asc'
|
||||
},
|
||||
{
|
||||
label: __('desc'),
|
||||
value: 'desc'
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (schema) {
|
||||
const formItems: Array<string> = [];
|
||||
schema.controls?.forEach(
|
||||
(item: any) =>
|
||||
item.name &&
|
||||
item.name !== 'orderBy' &&
|
||||
item.name !== 'order' &&
|
||||
formItems.push(item.name)
|
||||
);
|
||||
this.formItems = formItems;
|
||||
schema = {
|
||||
...schema,
|
||||
type: 'form',
|
||||
wrapperComponent: 'div',
|
||||
actions: [
|
||||
{
|
||||
type: 'button',
|
||||
label: __('reset'),
|
||||
actionType: 'clear-and-submit'
|
||||
},
|
||||
|
||||
{
|
||||
type: 'button',
|
||||
label: __('cancel'),
|
||||
actionType: 'cancel'
|
||||
},
|
||||
|
||||
{
|
||||
label: __('search'),
|
||||
type: 'submit',
|
||||
primary: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return schema || 'error';
|
||||
}
|
||||
|
||||
handleAction(e: any, action: Action, ctx: object, confirm: Function) {
|
||||
const {onAction} = this.props;
|
||||
|
||||
if (action.actionType === 'cancel' || action.actionType === 'close') {
|
||||
confirm();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.actionType === 'reset') {
|
||||
confirm();
|
||||
this.handleReset();
|
||||
return;
|
||||
}
|
||||
|
||||
onAction && onAction(e, action, ctx);
|
||||
}
|
||||
|
||||
handleReset() {
|
||||
const {onFilter, data, name, store} = this.props;
|
||||
const values = {...data};
|
||||
this.formItems.forEach(key => setVariable(values, key, undefined));
|
||||
|
||||
if (values.orderBy === name) {
|
||||
values.orderBy = '';
|
||||
values.order = 'asc';
|
||||
}
|
||||
|
||||
store.updateQuery(values);
|
||||
|
||||
onFilter && onFilter(values);
|
||||
}
|
||||
|
||||
handleSubmit(values: any, confirm: Function) {
|
||||
const {onFilter, name, store} = this.props;
|
||||
|
||||
if (values.order) {
|
||||
values = {
|
||||
...values,
|
||||
orderBy: name
|
||||
};
|
||||
}
|
||||
|
||||
store.updateQuery(values);
|
||||
|
||||
onFilter && onFilter(values);
|
||||
|
||||
confirm();
|
||||
}
|
||||
|
||||
isActive() {
|
||||
const {data, name, orderBy} = this.props;
|
||||
|
||||
return (orderBy && orderBy === name) || this.formItems.some(key => data?.[key]);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
render,
|
||||
name,
|
||||
data,
|
||||
searchable,
|
||||
store,
|
||||
orderBy,
|
||||
popOverContainer,
|
||||
classPrefix: ns,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
|
||||
const formSchema = this.buildSchema();
|
||||
const isActive = this.isActive();
|
||||
|
||||
return (
|
||||
<HeadCellDropDown
|
||||
className={`${ns}TableCell-searchBtn`}
|
||||
layerClassName={cx(
|
||||
`${ns}TableCell-searchPopOver`,
|
||||
(searchable as any).className
|
||||
)}
|
||||
active={isActive}
|
||||
filterIcon={<Icon icon="search" className="icon" />}
|
||||
popOverContainer={popOverContainer ? popOverContainer : () => findDOMNode(this)}
|
||||
filterDropdown={({setSelectedKeys, selectedKeys, confirm, clearFilters}) => {
|
||||
return render('quick-search-form', formSchema, {
|
||||
data: {
|
||||
...data,
|
||||
orderBy,
|
||||
order: orderBy && orderBy === name ? (store as ITableStore).order : ''
|
||||
},
|
||||
onSubmit: (values: object) => this.handleSubmit(values, confirm),
|
||||
onAction: (e: any, action: Action, ctx: object) => {
|
||||
this.handleAction(e, action, ctx, confirm);
|
||||
}
|
||||
}) as JSX.Element;
|
||||
}}>
|
||||
</HeadCellDropDown>
|
||||
);
|
||||
}
|
||||
}
|
19
src/renderers/Table-v2/TableCell.tsx
Normal file
19
src/renderers/Table-v2/TableCell.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import {Renderer} from '../../factory';
|
||||
import {TableCell} from '../Table';
|
||||
import QuickEdit from '../QuickEdit';
|
||||
import Copyable from '../Copyable';
|
||||
import PopOverable from '../PopOver';
|
||||
|
||||
@Renderer({
|
||||
type: 'cell-field',
|
||||
name: 'cell-field'
|
||||
})
|
||||
@PopOverable()
|
||||
@Copyable()
|
||||
@QuickEdit()
|
||||
export class CellFieldRenderer extends TableCell {
|
||||
static defaultProps = {
|
||||
...TableCell.defaultProps,
|
||||
wrapperComponent: 'div'
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,6 @@ export class HeadCellSearchDropDown extends React.Component<
|
||||
|
||||
this.open = this.open.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
this.close = this.close.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleAction = this.handleAction.bind(this);
|
||||
}
|
||||
|
@ -156,16 +156,14 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
classnames: cx,
|
||||
rows,
|
||||
prefixRowClassName,
|
||||
affixRowClassName,
|
||||
footable
|
||||
affixRowClassName
|
||||
} = this.props;
|
||||
|
||||
if (!(Array.isArray(items) && items.length)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 开启了footable,不需要考虑设置了breakpoint的列了
|
||||
const filterColumns = columns.filter(item => item.toggable && !(footable && item.breakpoint));
|
||||
const filterColumns = columns.filter(item => item.toggable);
|
||||
const result: any[] = [];
|
||||
|
||||
for (let index = 0; index < filterColumns.length; index++) {
|
||||
@ -184,16 +182,14 @@ export class TableBody extends React.Component<TableBodyProps> {
|
||||
}
|
||||
|
||||
// 缺少的单元格补齐
|
||||
// 考虑是否设置了
|
||||
// 开启了footable,不需要考虑设置了breakpoint的列了
|
||||
const appendLen =
|
||||
(footable ? columns.filter(item => !item.breakpoint).length : columns.length) - result.reduce((p, c) => p + (c.colSpan || 1), 0);
|
||||
columns.length - result.reduce((p, c) => p + (c.colSpan || 1), 0);
|
||||
|
||||
if (appendLen) {
|
||||
const item = result.pop();
|
||||
result.push({
|
||||
...item,
|
||||
colSpan: (item?.colSpan || 1) + appendLen
|
||||
colSpan: (item.colSpan || 1) + appendLen
|
||||
});
|
||||
}
|
||||
const ctx = createObject(data, {
|
||||
|
@ -3,11 +3,35 @@ import {
|
||||
getParent,
|
||||
Instance,
|
||||
SnapshotIn,
|
||||
isAlive
|
||||
isAlive,
|
||||
IAnyModelType,
|
||||
flow,
|
||||
getEnv
|
||||
} from 'mobx-state-tree';
|
||||
import find from 'lodash/find';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {isVisible, hasVisibleExpression} from '../utils/helper';
|
||||
import {iRendererStore} from './iRenderer';
|
||||
import {
|
||||
isVisible,
|
||||
hasVisibleExpression,
|
||||
isObjectShallowModified,
|
||||
qsstringify,
|
||||
guid,
|
||||
eachTree,
|
||||
createObject,
|
||||
flattenTree,
|
||||
isObject,
|
||||
immutableExtends,
|
||||
isEmpty,
|
||||
extendObject
|
||||
} from '../utils/helper';
|
||||
import {normalizeApiResponseData} from '../utils/api';
|
||||
import {Api, Payload, fetchOptions, ApiObject} from '../types';
|
||||
import {ServiceStore} from './service';
|
||||
|
||||
class ServerError extends Error {
|
||||
type = 'ServerError';
|
||||
}
|
||||
|
||||
export const Column = types
|
||||
.model('Column', {
|
||||
@ -18,7 +42,11 @@ export const Column = types
|
||||
pristine: types.optional(types.frozen(), undefined),
|
||||
toggable: true,
|
||||
index: 0,
|
||||
type: ''
|
||||
type: '',
|
||||
children: types.optional(
|
||||
types.array(types.late((): IAnyModelType => Column)),
|
||||
[]
|
||||
)
|
||||
})
|
||||
.actions(self => ({
|
||||
toggleToggle() {
|
||||
@ -41,29 +69,133 @@ export type SColumn = SnapshotIn<typeof Column>;
|
||||
|
||||
export const Row = types
|
||||
.model('Row', {
|
||||
data: types.frozen({} as any)
|
||||
});
|
||||
storeType: 'Row',
|
||||
id: types.identifier,
|
||||
key: types.string,
|
||||
pristine: types.frozen({} as any), // 原始数据
|
||||
data: types.frozen({} as any),
|
||||
index: types.number,
|
||||
newIndex: types.number,
|
||||
depth: types.number, // 当前children位于第几层,便于使用getParent获取最顶层TableStore
|
||||
children: types.optional(
|
||||
types.array(types.late((): IAnyModelType => Row)),
|
||||
[]
|
||||
),
|
||||
path: '' // 行数据的位置
|
||||
})
|
||||
.views(self => ({
|
||||
get checked(): boolean {
|
||||
return (getParent(self, self.depth * 2) as ITableStore).isSelected(
|
||||
self as IRow
|
||||
);
|
||||
},
|
||||
|
||||
get modified() {
|
||||
if (!self.data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object.keys(self.data).some(
|
||||
key => !isEqual(self.data[key], self.pristine[key])
|
||||
);
|
||||
},
|
||||
|
||||
get locals(): any {
|
||||
let children: Array<any> | null = null;
|
||||
if (self.children.length) {
|
||||
children = self.children.map(item => item.locals);
|
||||
}
|
||||
|
||||
const parent = getParent(self, 2) as ITableStore;
|
||||
return createObject(
|
||||
extendObject((getParent(self, self.depth * 2) as ITableStore).data, {
|
||||
index: self.index,
|
||||
// todo 以后再支持多层,目前先一层
|
||||
parent: parent.storeType === Row.name ? parent.data : undefined
|
||||
}),
|
||||
children
|
||||
? {
|
||||
...self.data,
|
||||
children
|
||||
}
|
||||
: self.data
|
||||
);
|
||||
}
|
||||
}))
|
||||
.actions(self => ({
|
||||
replaceWith(data: any) {
|
||||
Object.keys(data).forEach(key => {
|
||||
if (key !== 'id') {
|
||||
(self as any)[key] = data[key];
|
||||
}
|
||||
});
|
||||
|
||||
if (Array.isArray(data.children)) {
|
||||
const arr = data.children;
|
||||
const pool = arr.concat();
|
||||
|
||||
// 把多的删了先
|
||||
if (self.children.length > arr.length) {
|
||||
self.children.splice(arr.length, self.children.length - arr.length);
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
const len = self.children.length;
|
||||
while (pool.length) {
|
||||
// 因为父级id未更新,所以需要将子级的parentId正确指向父级id
|
||||
const item = {
|
||||
...pool.shift(),
|
||||
parentId: self.id
|
||||
}!;
|
||||
|
||||
if (index < len) {
|
||||
self.children[index].replaceWith(item);
|
||||
} else {
|
||||
const row = Row.create(item);
|
||||
self.children.push(row);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
},
|
||||
change(values: object, savePristine?: boolean) {
|
||||
self.data = immutableExtends(self.data, values);
|
||||
savePristine && (self.pristine = self.data);
|
||||
}
|
||||
}));
|
||||
|
||||
export type IRow = Instance<typeof Row>;
|
||||
export type SRow = SnapshotIn<typeof Row>;
|
||||
|
||||
export const TableStoreV2 = iRendererStore
|
||||
export const TableStoreV2 = ServiceStore
|
||||
.named('TableStoreV2')
|
||||
.props({
|
||||
columns: types.array(Column),
|
||||
rows: types.array(Row),
|
||||
columnsToggable: types.optional(
|
||||
selectedRowKeys: types.array(types.frozen()),
|
||||
selectedRows: types.array(types.reference(Row)),
|
||||
expandedRowKeys: types.array(types.frozen()),
|
||||
columnsTogglable: types.optional(
|
||||
types.union(types.boolean, types.literal('auto')),
|
||||
'auto'
|
||||
)
|
||||
),
|
||||
orderBy: '',
|
||||
order: types.optional(
|
||||
types.union(types.literal('asc'), types.literal('desc')),
|
||||
'asc'
|
||||
),
|
||||
query: types.optional(types.frozen(), {}),
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
dragging: false
|
||||
})
|
||||
.views(self => {
|
||||
function getToggable() {
|
||||
if (self.columnsToggable === 'auto') {
|
||||
if (self.columnsTogglable === 'auto') {
|
||||
return self.columns.filter.length > 10;
|
||||
}
|
||||
|
||||
return self.columnsToggable;
|
||||
return self.columnsTogglable;
|
||||
}
|
||||
|
||||
function hasColumnHidden() {
|
||||
@ -80,16 +212,43 @@ export const TableStoreV2 = iRendererStore
|
||||
return getToggableColumns().filter(item => item.toggled);
|
||||
}
|
||||
|
||||
function getAllFilteredColumns(columns?: Array<SColumn>): Array<any> {
|
||||
if (columns) {
|
||||
return columns.filter(
|
||||
item =>
|
||||
item &&
|
||||
isVisible(
|
||||
item.pristine,
|
||||
hasVisibleExpression(item.pristine) ? self.data : {}
|
||||
) &&
|
||||
(item.toggled || !item.toggable)
|
||||
).map(item => ({
|
||||
...item.pristine,
|
||||
type: item.type,
|
||||
children: item.children ? getAllFilteredColumns(item.children) : undefined
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getFilteredColumns() {
|
||||
return self.columns.filter(
|
||||
item =>
|
||||
item &&
|
||||
isVisible(
|
||||
item.pristine,
|
||||
hasVisibleExpression(item.pristine) ? self.data : {}
|
||||
) &&
|
||||
(item.toggled || !item.toggable)
|
||||
).map(item => ({...item.pristine, type: item.type}));
|
||||
return getAllFilteredColumns(self.columns);
|
||||
}
|
||||
|
||||
function getUnSelectedRows() {
|
||||
return flattenTree<IRow>(self.rows).filter((item: IRow) => !item.checked);
|
||||
}
|
||||
|
||||
function getData(superData: any): any {
|
||||
return createObject(superData, {
|
||||
items: self.rows.map(item => item.data),
|
||||
selectedItems: self.selectedRows.map(item => item.data),
|
||||
unSelectedItems: getUnSelectedRows().map(item => item.data)
|
||||
});
|
||||
}
|
||||
|
||||
function isSelected(row: IRow): boolean {
|
||||
return !!~self.selectedRows.indexOf(row);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -113,32 +272,63 @@ export const TableStoreV2 = iRendererStore
|
||||
return getActiveToggableColumns();
|
||||
},
|
||||
|
||||
get dataSource() {
|
||||
return self.rows.map(item => item.data);
|
||||
},
|
||||
|
||||
get currentSelectedRowKeys() {
|
||||
return self.selectedRowKeys.map(item => item);
|
||||
},
|
||||
|
||||
get currentExpandedKeys() {
|
||||
return self.expandedRowKeys.map(item => item);
|
||||
},
|
||||
|
||||
// 是否隐藏了某列
|
||||
hasColumnHidden() {
|
||||
return hasColumnHidden();
|
||||
}
|
||||
},
|
||||
|
||||
getData,
|
||||
|
||||
isSelected
|
||||
}
|
||||
})
|
||||
.actions(self => {
|
||||
function update(config: Partial<STableStore>) {
|
||||
config.columnsToggable !== void 0 &&
|
||||
(self.columnsToggable = config.columnsToggable);
|
||||
|
||||
if (config.columns && Array.isArray(config.columns)) {
|
||||
let columns: Array<SColumn> = config.columns
|
||||
function updateColumns(columns: Array<SColumn>) {
|
||||
if (columns && Array.isArray(columns)) {
|
||||
let cols: Array<SColumn> = columns
|
||||
.filter(column => column)
|
||||
.concat();
|
||||
|
||||
columns = columns.map((item, index) => ({
|
||||
...item,
|
||||
cols = cols.map((item, index) => ({
|
||||
...item,
|
||||
index,
|
||||
type: item.type || 'plain',
|
||||
pristine: item,
|
||||
toggled: item.toggled !== false,
|
||||
breakpoint: item.breakpoint
|
||||
breakpoint: item.breakpoint,
|
||||
children: item.children ? updateColumns(item.children) : []
|
||||
}));
|
||||
|
||||
self.columns.replace(columns as any);
|
||||
return cols;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function update(config: Partial<STableStore>) {
|
||||
config.columnsTogglable !== void 0 &&
|
||||
(self.columnsTogglable = config.columnsTogglable);
|
||||
|
||||
if (typeof config.orderBy === 'string') {
|
||||
setOrderByInfo(
|
||||
config.orderBy,
|
||||
config.order === 'desc' ? 'desc' : 'asc'
|
||||
);
|
||||
}
|
||||
|
||||
if (config.columns && Array.isArray(config.columns)) {
|
||||
self.columns.replace(updateColumns(config.columns) as any);
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,9 +343,275 @@ export const TableStoreV2 = iRendererStore
|
||||
);
|
||||
}
|
||||
|
||||
function setOrderByInfo(key: string, direction: 'asc' | 'desc') {
|
||||
self.orderBy = key;
|
||||
self.order = direction;
|
||||
}
|
||||
|
||||
function updateQuery(
|
||||
values: object,
|
||||
updater?: Function,
|
||||
pageNoField: string = 'pageNo',
|
||||
pageSizeField: string = 'pageSize',
|
||||
replace: boolean = false
|
||||
) {
|
||||
const originQuery = self.query;
|
||||
self.query = replace
|
||||
? {
|
||||
...values
|
||||
}
|
||||
: {
|
||||
...self.query,
|
||||
...values
|
||||
};
|
||||
|
||||
if (self.query[pageNoField || 'pageNo']) {
|
||||
self.pageNo = parseInt(self.query[pageNoField || 'pageNo'], 10);
|
||||
}
|
||||
|
||||
if (self.query[pageSizeField || 'pageSize']) {
|
||||
self.pageSize = parseInt(self.query[pageSizeField || 'pageSize'], 10);
|
||||
}
|
||||
|
||||
updater &&
|
||||
isObjectShallowModified(originQuery, self.query, false) &&
|
||||
setTimeout(updater.bind(null, `?${qsstringify(self.query)}`), 4);
|
||||
}
|
||||
|
||||
function updateSelectedRows(rows: Array<any>, selectedKeys: Array<any>, keyField?: string) {
|
||||
eachTree(rows, item => {
|
||||
if (~selectedKeys.indexOf(item.pristine[keyField || 'key'])) {
|
||||
self.selectedRows.push(item.id);
|
||||
self.selectedRowKeys.push(item.pristine[keyField || 'key']);
|
||||
} else if (
|
||||
find(
|
||||
selectedKeys,
|
||||
a =>
|
||||
a &&
|
||||
a == item.pristine[keyField || 'key']
|
||||
)
|
||||
) {
|
||||
self.selectedRows.push(item.id);
|
||||
self.selectedRowKeys.push(item.pristine[keyField || 'key']);
|
||||
} else if (item.children) {
|
||||
updateSelectedRows(item.children, selectedKeys, keyField);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelected(selectedKeys: Array<any>, keyField?: string) {
|
||||
self.selectedRows.clear();
|
||||
self.selectedRowKeys.clear();
|
||||
|
||||
updateSelectedRows(self.rows, selectedKeys, keyField);
|
||||
}
|
||||
|
||||
function updateExpanded(expandedRowKeys: Array<any>, keyField?: string) {
|
||||
self.expandedRowKeys.clear();
|
||||
|
||||
eachTree(self.rows, item => {
|
||||
if (~expandedRowKeys.indexOf(item.pristine[keyField || 'key'])) {
|
||||
self.expandedRowKeys.push(item.pristine[keyField || 'key']);
|
||||
} else if (
|
||||
find(
|
||||
expandedRowKeys,
|
||||
a =>
|
||||
a &&
|
||||
a == item.pristine[keyField || 'key']
|
||||
)
|
||||
) {
|
||||
self.expandedRowKeys.push(item.pristine[keyField || 'key']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 尽可能的复用 row
|
||||
function replaceRow(arr: Array<SRow>, reUseRow?: boolean) {
|
||||
if (reUseRow === false) {
|
||||
self.rows.replace(arr.map(item => Row.create(item)));
|
||||
return;
|
||||
}
|
||||
|
||||
const pool = arr.concat();
|
||||
|
||||
// 把多的删了先
|
||||
if (self.rows.length > arr.length) {
|
||||
self.rows.splice(arr.length, self.rows.length - arr.length);
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
const len = self.rows.length;
|
||||
while (pool.length) {
|
||||
const item = pool.shift()!;
|
||||
|
||||
if (index < len) {
|
||||
self.rows[index].replaceWith(item);
|
||||
} else {
|
||||
const row = Row.create(item);
|
||||
self.rows.push(row);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
function initChildren(
|
||||
children: Array<any>,
|
||||
depth: number,
|
||||
pindex: number,
|
||||
parentId: string,
|
||||
path: string = '',
|
||||
keyField?: string
|
||||
): any {
|
||||
const key = keyField || 'children';
|
||||
|
||||
depth += 1;
|
||||
return children.map((item, index) => {
|
||||
item = isObject(item)
|
||||
? item
|
||||
: {
|
||||
item
|
||||
};
|
||||
const id = guid();
|
||||
|
||||
return {
|
||||
id: id,
|
||||
parentId,
|
||||
key: String(`${pindex}-${depth}-${index}`),
|
||||
path: `${path}${index}`,
|
||||
depth: depth,
|
||||
index: index,
|
||||
newIndex: index,
|
||||
pristine: item,
|
||||
data: item,
|
||||
rowSpans: {},
|
||||
children:
|
||||
item && Array.isArray(item[key])
|
||||
? initChildren(
|
||||
item[key],
|
||||
depth,
|
||||
index,
|
||||
id,
|
||||
`${path}${index}.`,
|
||||
|
||||
)
|
||||
: []
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function initRows(
|
||||
rows: Array<any>,
|
||||
getEntryId?: (entry: any, index: number) => string,
|
||||
reUseRow?: boolean,
|
||||
keyField?: string
|
||||
) {
|
||||
self.selectedRows.clear();
|
||||
|
||||
const key = keyField || 'children';
|
||||
|
||||
let arr: Array<SRow> = rows.map((item, index) => {
|
||||
let id = getEntryId ? getEntryId(item, index) : guid();
|
||||
|
||||
return {
|
||||
id: id,
|
||||
key: String(`${index}-1-${index}`),
|
||||
index: index,
|
||||
newIndex: index,
|
||||
pristine: item,
|
||||
path: `${index}`,
|
||||
data: item,
|
||||
depth: 1, // 最大父节点默认为第一层,逐层叠加
|
||||
children:
|
||||
item && Array.isArray(item[key])
|
||||
? initChildren(item[key], 1, index, id, `${index}.`, key)
|
||||
: []
|
||||
};
|
||||
});
|
||||
|
||||
replaceRow(arr, reUseRow);
|
||||
}
|
||||
|
||||
const saveRemote: (
|
||||
api: Api,
|
||||
data?: object,
|
||||
options?: fetchOptions
|
||||
) => Promise<any> = flow(function* saveRemote(
|
||||
api: Api,
|
||||
data: object,
|
||||
options: fetchOptions = {}
|
||||
) {
|
||||
try {
|
||||
options = {
|
||||
method: 'post', // 默认走 post
|
||||
...options
|
||||
};
|
||||
|
||||
self.markSaving(true);
|
||||
const json: Payload = yield getEnv(self).fetcher(api, data, options);
|
||||
self.markSaving(false);
|
||||
|
||||
if (!isEmpty(json.data) || json.ok) {
|
||||
self.updateData(
|
||||
normalizeApiResponseData(json.data),
|
||||
{
|
||||
__saved: Date.now()
|
||||
},
|
||||
!!api && (api as ApiObject).replaceData
|
||||
);
|
||||
self.updatedAt = Date.now();
|
||||
}
|
||||
|
||||
if (!json.ok) {
|
||||
self.updateMessage(
|
||||
json.msg ?? options.errorMessage ?? self.__('saveFailed'),
|
||||
true
|
||||
);
|
||||
getEnv(self).notify(
|
||||
'error',
|
||||
self.msg,
|
||||
json.msgTimeout !== undefined
|
||||
? {
|
||||
closeButton: true,
|
||||
timeout: json.msgTimeout
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
throw new ServerError(self.msg);
|
||||
} else {
|
||||
self.updateMessage(json.msg ?? options.successMessage);
|
||||
self.msg &&
|
||||
getEnv(self).notify(
|
||||
'success',
|
||||
self.msg,
|
||||
json.msgTimeout !== undefined
|
||||
? {
|
||||
closeButton: true,
|
||||
timeout: json.msgTimeout
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
return json.data;
|
||||
} catch (e) {
|
||||
self.markSaving(false);
|
||||
|
||||
if (!isAlive(self) || self.disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.type !== 'ServerError' && getEnv(self).notify('error', e.message);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
update,
|
||||
persistSaveToggledColumns,
|
||||
setOrderByInfo,
|
||||
updateQuery,
|
||||
initRows,
|
||||
updateSelected,
|
||||
updateExpanded,
|
||||
|
||||
// events
|
||||
afterCreate() {
|
||||
@ -177,6 +633,11 @@ export const TableStoreV2 = iRendererStore
|
||||
);
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
saveRemote,
|
||||
|
||||
getRowByIndex(rowIndex: number) {
|
||||
return self.rows[rowIndex];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -639,8 +639,7 @@ export const TableStore = iRendererStore
|
||||
},
|
||||
|
||||
get disabledHeadCheckbox() {
|
||||
// 设置为multiple 默认没选择会报错
|
||||
const selectedLength = self.data?.selectedItems?.length;
|
||||
const selectedLength = self.data?.selectedItems.length;
|
||||
const maxLength = self.maxKeepItemSelectionLength;
|
||||
|
||||
if (!self.data || !self.keepItemSelectionOnPageChange || !maxLength) {
|
||||
|
Loading…
Reference in New Issue
Block a user