@ant-design/icons#FilterOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#FilterOutlined.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ActionFilterRow.tsx From posthog-foss with MIT License | 4 votes |
export function ActionFilterRow({
logic,
filter,
index,
hideMathSelector,
hidePropertySelector,
singleFilter,
showOr,
hideFilter,
hideRename,
onRenameClick = () => {},
showSeriesIndicator,
seriesIndicatorType = 'alpha',
horizontalUI = false,
fullWidth = false,
filterCount,
customRowPrefix,
customRowSuffix,
rowClassName,
propertyFilterWrapperClassName,
stripeActionRow = true,
hasBreakdown,
showNestedArrow = false,
hideDeleteBtn = false,
actionsTaxonomicGroupTypes = [TaxonomicFilterGroupType.Events, TaxonomicFilterGroupType.Actions],
propertiesTaxonomicGroupTypes,
disabled = false,
renderRow,
}: ActionFilterRowProps): JSX.Element {
const { selectedFilter, entities, entityFilterVisible } = useValues(logic)
const {
updateFilter,
selectFilter,
updateFilterMath,
removeLocalFilter,
updateFilterProperty,
setEntityFilterVisibility,
duplicateFilter,
} = useActions(logic)
const { numericalPropertyNames } = useValues(propertyDefinitionsModel)
const { actions } = useValues(actionsModel)
const { mathDefinitions } = useValues(mathsLogic)
const visible = typeof filter.order === 'number' ? entityFilterVisible[filter.order] : false
let entity: BareEntity, name: string | null | undefined, value: PropertyFilterValue
const { math, math_property: mathProperty, math_group_type_index: mathGroupTypeIndex } = filter
const onClose = (): void => {
removeLocalFilter({ ...filter, index })
}
const onMathSelect = (_: unknown, selectedMath: string): void => {
updateFilterMath({
...mathTypeToApiValues(selectedMath),
math_property: mathDefinitions[selectedMath]?.onProperty ? mathProperty ?? '$time' : undefined,
type: filter.type,
index,
})
}
const onMathPropertySelect = (_: unknown, property: string): void => {
updateFilterMath({
...filter,
math_property: property,
index,
})
}
const dropDownCondition = Boolean(
selectedFilter && selectedFilter?.type === filter.type && selectedFilter?.index === index
)
const onClick = (): void => {
if (dropDownCondition) {
selectFilter(null)
} else {
selectFilter({ ...filter, index })
}
}
if (filter.type === EntityTypes.NEW_ENTITY) {
name = null
value = null
} else {
entity = (entities[filter.type] as BareEntity[])?.filter((action) => action.id === filter.id)[0] || {}
name = entity.name || filter.name
value = entity.id || filter.id
}
const orLabel = <div className="stateful-badge or width-locked">OR</div>
const seriesIndicator =
seriesIndicatorType === 'numeric' ? (
<SeriesGlyph style={{ borderColor: 'var(--border)' }}>{index + 1}</SeriesGlyph>
) : (
<SeriesLetter seriesIndex={index} hasBreakdown={hasBreakdown} />
)
const prefix = typeof customRowPrefix === 'function' ? customRowPrefix({ filter, index, onClose }) : customRowPrefix
const filterElement = (
<Popup
overlay={
<TaxonomicFilter
groupType={filter.type as TaxonomicFilterGroupType}
value={
filter.type === 'actions' && typeof value === 'string' ? parseInt(value) : value || undefined
}
onChange={(taxonomicGroup, changedValue, item) => {
updateFilter({
type: taxonomicFilterGroupTypeToEntityType(taxonomicGroup.type) || undefined,
id: `${changedValue}`,
name: item?.name,
index,
})
}}
onClose={() => selectFilter(null)}
taxonomicGroupTypes={actionsTaxonomicGroupTypes}
/>
}
visible={dropDownCondition}
onClickOutside={() => selectFilter(null)}
>
{({ setRef }) => (
<Button
data-attr={'trend-element-subject-' + index}
onClick={onClick}
block={fullWidth}
ref={setRef}
disabled={disabled}
style={{
maxWidth: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<span className="text-overflow" style={{ maxWidth: '100%' }}>
<EntityFilterInfo filter={filter} />
</span>
<DownOutlined style={{ fontSize: 10 }} />
</Button>
)}
</Popup>
)
const suffix = typeof customRowSuffix === 'function' ? customRowSuffix({ filter, index, onClose }) : customRowSuffix
const propertyFiltersButton = (
<Button
type="link"
onClick={() => {
typeof filter.order === 'number' ? setEntityFilterVisibility(filter.order, !visible) : undefined
}}
className={`row-action-btn show-filters${visible ? ' visible' : ''}`}
data-attr={'show-prop-filter-' + index}
title="Show filters"
>
<FilterOutlined />
{filter.properties?.length ? pluralize(filter.properties?.length, 'filter') : null}
</Button>
)
const renameRowButton = (
<Button
type="link"
onClick={() => {
selectFilter(filter)
onRenameClick()
}}
className={`row-action-btn show-rename`}
data-attr={'show-prop-rename-' + index}
title="Rename graph series"
>
<EditOutlined />
</Button>
)
const duplicateRowButton = (
<Button
type="link"
onClick={() => {
duplicateFilter(filter)
}}
className={`row-action-btn show-duplicabe`}
data-attr={'show-prop-duplicate-' + index}
title="Duplicate graph series"
>
<CopyOutlined />
</Button>
)
const deleteButton = (
<Button
type="link"
onClick={onClose}
className="row-action-btn delete"
data-attr={'delete-prop-filter-' + index}
title="Delete graph series"
>
<DeleteOutlined />
</Button>
)
return (
<div
className={clsx({
'action-row-striped': horizontalUI && stripeActionRow,
'action-row': !horizontalUI || !stripeActionRow,
'full-width': fullWidth,
})}
>
{!horizontalUI && index > 0 && showOr && (
<Row align="middle" style={{ marginTop: 12 }}>
{orLabel}
</Row>
)}
<Row gutter={8} align="middle" className={`${!horizontalUI ? 'mt' : ''} ${rowClassName}`} wrap={!fullWidth}>
{renderRow ? (
renderRow({
seriesIndicator,
prefix,
filter: filterElement,
suffix,
propertyFiltersButton: propertyFiltersButton,
renameRowButton,
deleteButton,
orLabel,
})
) : (
<>
{!hideDeleteBtn && horizontalUI && !singleFilter && filterCount > 1 && (
<Col>
<Button
type="link"
onClick={onClose}
className="row-action-btn delete"
title="Remove graph series"
danger
icon={<CloseSquareOutlined />}
/>
</Col>
)}
{showSeriesIndicator && <Col className="action-row-letter">{seriesIndicator}</Col>}
{customRowPrefix !== undefined ? (
<Col>{prefix}</Col>
) : (
<>{horizontalUI && <Col>Showing</Col>}</>
)}
<Col
className="column-filter"
style={fullWidth ? {} : { maxWidth: `calc(${hideMathSelector ? '100' : '50'}% - 16px)` }}
flex={fullWidth ? 'auto' : undefined}
>
{filterElement}
</Col>
{customRowSuffix !== undefined && <Col className="column-row-suffix">{suffix}</Col>}
{!hideMathSelector && (
<>
{horizontalUI && <Col>counted by</Col>}
<Col style={{ maxWidth: `calc(50% - 16px${showSeriesIndicator ? ' - 32px' : ''})` }}>
<MathSelector
math={math}
mathGroupTypeIndex={mathGroupTypeIndex}
index={index}
onMathSelect={onMathSelect}
areEventPropertiesNumericalAvailable={!!numericalPropertyNames.length}
style={{ maxWidth: '100%', width: 'initial' }}
/>
</Col>
{mathDefinitions[math || '']?.onProperty && (
<>
{horizontalUI && <Col>on property</Col>}
<Col
style={{
maxWidth: `calc(50% - 16px${showSeriesIndicator ? ' - 32px' : ''})`,
}}
>
<MathPropertySelector
name={name}
math={math}
mathProperty={mathProperty}
index={index}
onMathPropertySelect={onMathPropertySelect}
properties={numericalPropertyNames}
horizontalUI={horizontalUI}
/>
</Col>
</>
)}
</>
)}
{(horizontalUI || fullWidth) && !hideFilter && <Col>{propertyFiltersButton}</Col>}
{(horizontalUI || fullWidth) && !hideRename && <Col>{renameRowButton}</Col>}
{(horizontalUI || fullWidth) && !hideFilter && !singleFilter && <Col>{duplicateRowButton}</Col>}
{!hideDeleteBtn && !horizontalUI && !singleFilter && (
<Col className="column-delete-btn">{deleteButton}</Col>
)}
{horizontalUI && filterCount > 1 && index < filterCount - 1 && showOr && orLabel}
</>
)}
</Row>
{(!hidePropertySelector || (filter.properties && filter.properties.length > 0)) &&
!horizontalUI &&
!fullWidth && (
<div style={{ paddingTop: 6 }}>
<span style={{ color: '#C4C4C4', fontSize: 18, paddingLeft: 6, paddingRight: 2 }}>↳</span>
<Button
className="ant-btn-md"
onClick={() =>
typeof filter.order === 'number'
? setEntityFilterVisibility(filter.order, !visible)
: undefined
}
data-attr={'show-prop-filter-' + index}
>
{determineFilterLabel(visible, filter)}
</Button>
</div>
)}
{visible && (
<div
className={
propertyFilterWrapperClassName
? `mr property-filter-wrapper ${propertyFilterWrapperClassName}`
: 'mr property-filter-wrapper'
}
>
<PropertyFilters
pageKey={`${index}-${value}-filter`}
propertyFilters={filter.properties}
onChange={(properties) => updateFilterProperty({ properties, index })}
disablePopover={horizontalUI}
style={{ marginBottom: 0 }}
showNestedArrow={showNestedArrow}
taxonomicGroupTypes={propertiesTaxonomicGroupTypes}
eventNames={
filter.type === TaxonomicFilterGroupType.Events && filter.id
? [String(filter.id)]
: filter.type === TaxonomicFilterGroupType.Actions && filter.id
? getEventNamesForAction(parseInt(String(filter.id)), actions)
: []
}
/>
</div>
)}
</div>
)
}
Example #2
Source File: SessionRecordingsTable.tsx From posthog-foss with MIT License | 4 votes |
export function SessionRecordingsTable({ personUUID, isPersonPage = false }: SessionRecordingsTableProps): JSX.Element {
const sessionRecordingsTableLogicInstance = sessionRecordingsTableLogic({ personUUID })
const {
sessionRecordings,
sessionRecordingsResponseLoading,
sessionRecordingId,
entityFilters,
propertyFilters,
hasNext,
hasPrev,
fromDate,
toDate,
durationFilter,
showFilters,
} = useValues(sessionRecordingsTableLogicInstance)
const {
openSessionPlayer,
closeSessionPlayer,
setEntityFilters,
setPropertyFilters,
loadNext,
loadPrev,
setDateRange,
setDurationFilter,
enableFilter,
} = useActions(sessionRecordingsTableLogicInstance)
const { preflight } = useValues(preflightLogic)
const columns: LemonTableColumns<SessionRecordingType> = [
{
title: 'Start time',
render: function RenderStartTime(_: any, sessionRecording: SessionRecordingType) {
return <TZLabel time={sessionRecording.start_time} formatString="MMMM DD, YYYY h:mm" />
},
},
{
title: 'Duration',
render: function RenderDuration(_: any, sessionRecording: SessionRecordingType) {
return <span>{humanFriendlyDuration(sessionRecording.recording_duration)}</span>
},
},
{
title: 'Person',
key: 'person',
render: function RenderPersonLink(_: any, sessionRecording: SessionRecordingType) {
return <PersonHeader withIcon person={sessionRecording.person} />
},
},
{
render: function RenderPlayButton(_: any, sessionRecording: SessionRecordingType) {
return (
<div className="play-button-container">
<Button
className={sessionRecording.viewed ? 'play-button viewed' : 'play-button'}
data-attr="session-recordings-button"
icon={<PlayCircleOutlined />}
>
Watch recording
</Button>
</div>
)
},
},
]
return (
<div className="session-recordings-table" data-attr="session-recordings-table">
<Row className="filter-row">
<div className="filter-container" style={{ display: showFilters ? undefined : 'none' }}>
<div>
<Typography.Text strong>
{`Filter by events and actions `}
<Tooltip title="Show recordings where all of the events or actions listed below happen.">
<InfoCircleOutlined className="info-icon" />
</Tooltip>
</Typography.Text>
<ActionFilter
fullWidth={true}
filters={entityFilters}
setFilters={(payload) => {
setEntityFilters(payload)
}}
typeKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
hideMathSelector={true}
buttonCopy="Add another filter"
horizontalUI
stripeActionRow={false}
propertyFilterWrapperClassName="session-recording-action-property-filter"
customRowPrefix=""
hideRename
showOr
renderRow={(props) => <FilterRow {...props} />}
showNestedArrow={false}
actionsTaxonomicGroupTypes={[
TaxonomicFilterGroupType.Actions,
TaxonomicFilterGroupType.Events,
]}
propertiesTaxonomicGroupTypes={[
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.Elements,
]}
/>
</div>
{!isPersonPage && preflight?.is_clickhouse_enabled && (
<div className="mt-2">
<Typography.Text strong>
{`Filter by persons and cohorts `}
<Tooltip title="Show recordings by persons who match the set criteria">
<InfoCircleOutlined className="info-icon" />
</Tooltip>
</Typography.Text>
<PropertyFilters
popoverPlacement="bottomRight"
pageKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
taxonomicGroupTypes={[
TaxonomicFilterGroupType.PersonProperties,
TaxonomicFilterGroupType.Cohorts,
]}
propertyFilters={propertyFilters}
onChange={(properties) => {
setPropertyFilters(properties)
}}
/>
</div>
)}
</div>
<Button
style={{ display: showFilters ? 'none' : undefined }}
onClick={() => {
enableFilter()
if (isPersonPage) {
const entityFilterButtons = document.querySelectorAll('.entity-filter-row button')
if (entityFilterButtons.length > 0) {
;(entityFilterButtons[0] as HTMLElement).click()
}
}
}}
>
<FilterOutlined /> Filter recordings
</Button>
<Row className="time-filter-row">
<Row className="time-filter">
<DateFilter
makeLabel={(key) => (
<>
<CalendarOutlined />
<span> {key}</span>
</>
)}
defaultValue="Last 7 days"
bordered={true}
dateFrom={fromDate ?? undefined}
dateTo={toDate ?? undefined}
onChange={(changedDateFrom, changedDateTo) => {
setDateRange(changedDateFrom, changedDateTo)
}}
dateOptions={{
Custom: { values: [] },
'Last 24 hours': { values: ['-24h'] },
'Last 7 days': { values: ['-7d'] },
'Last 21 days': { values: ['-21d'] },
}}
/>
</Row>
<Row className="time-filter">
<Typography.Text className="filter-label">Duration</Typography.Text>
<DurationFilter
onChange={(newFilter) => {
setDurationFilter(newFilter)
}}
initialFilter={durationFilter}
pageKey={isPersonPage ? `person-${personUUID}` : 'session-recordings'}
/>
</Row>
</Row>
</Row>
<LemonTable
dataSource={sessionRecordings}
columns={columns}
loading={sessionRecordingsResponseLoading}
onRow={(sessionRecording) => ({
onClick: (e) => {
// Lets the link to the person open the person's page and not the session recording
if (!(e.target as HTMLElement).closest('a')) {
openSessionPlayer(sessionRecording.id, RecordingWatchedSource.RecordingsList)
}
},
})}
rowClassName="cursor-pointer"
data-attr="session-recording-table"
/>
{(hasPrev || hasNext) && (
<Row className="pagination-control">
<Button
type="link"
disabled={!hasPrev}
onClick={() => {
loadPrev()
window.scrollTo(0, 0)
}}
>
<LeftOutlined /> Previous
</Button>
<Button
type="link"
disabled={!hasNext}
onClick={() => {
loadNext()
window.scrollTo(0, 0)
}}
>
Next <RightOutlined />
</Button>
</Row>
)}
<div style={{ marginBottom: 64 }} />
{!!sessionRecordingId && <SessionPlayerDrawer isPersonPage={isPersonPage} onClose={closeSessionPlayer} />}
</div>
)
}
Example #3
Source File: Icon.tsx From html2sketch with MIT License | 4 votes |
IconSymbol: FC = () => {
return (
<Row>
{/*<CaretUpOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
{/*/>*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
{/*/>*/}
{/*<StepBackwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
{/*<StepForwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
<StepForwardOutlined />
<ShrinkOutlined />
<ArrowsAltOutlined />
<DownOutlined />
<UpOutlined />
<LeftOutlined />
<RightOutlined />
<CaretUpOutlined />
<CaretDownOutlined />
<CaretLeftOutlined />
<CaretRightOutlined />
<VerticalAlignTopOutlined />
<RollbackOutlined />
<FastBackwardOutlined />
<FastForwardOutlined />
<DoubleRightOutlined />
<DoubleLeftOutlined />
<VerticalLeftOutlined />
<VerticalRightOutlined />
<VerticalAlignMiddleOutlined />
<VerticalAlignBottomOutlined />
<ForwardOutlined />
<BackwardOutlined />
<EnterOutlined />
<RetweetOutlined />
<SwapOutlined />
<SwapLeftOutlined />
<SwapRightOutlined />
<ArrowUpOutlined />
<ArrowDownOutlined />
<ArrowLeftOutlined />
<ArrowRightOutlined />
<LoginOutlined />
<LogoutOutlined />
<MenuFoldOutlined />
<MenuUnfoldOutlined />
<BorderBottomOutlined />
<BorderHorizontalOutlined />
<BorderInnerOutlined />
<BorderOuterOutlined />
<BorderLeftOutlined />
<BorderRightOutlined />
<BorderTopOutlined />
<BorderVerticleOutlined />
<PicCenterOutlined />
<PicLeftOutlined />
<PicRightOutlined />
<RadiusBottomleftOutlined />
<RadiusBottomrightOutlined />
<RadiusUpleftOutlined />
<RadiusUprightOutlined />
<FullscreenOutlined />
<FullscreenExitOutlined />
<QuestionOutlined />
<PauseOutlined />
<MinusOutlined />
<PauseCircleOutlined />
<InfoOutlined />
<CloseOutlined />
<ExclamationOutlined />
<CheckOutlined />
<WarningOutlined />
<IssuesCloseOutlined />
<StopOutlined />
<EditOutlined />
<CopyOutlined />
<ScissorOutlined />
<DeleteOutlined />
<SnippetsOutlined />
<DiffOutlined />
<HighlightOutlined />
<AlignCenterOutlined />
<AlignLeftOutlined />
<AlignRightOutlined />
<BgColorsOutlined />
<BoldOutlined />
<ItalicOutlined />
<UnderlineOutlined />
<StrikethroughOutlined />
<RedoOutlined />
<UndoOutlined />
<ZoomInOutlined />
<ZoomOutOutlined />
<FontColorsOutlined />
<FontSizeOutlined />
<LineHeightOutlined />
<SortAscendingOutlined />
<SortDescendingOutlined />
<DragOutlined />
<OrderedListOutlined />
<UnorderedListOutlined />
<RadiusSettingOutlined />
<ColumnWidthOutlined />
<ColumnHeightOutlined />
<AreaChartOutlined />
<PieChartOutlined />
<BarChartOutlined />
<DotChartOutlined />
<LineChartOutlined />
<RadarChartOutlined />
<HeatMapOutlined />
<FallOutlined />
<RiseOutlined />
<StockOutlined />
<BoxPlotOutlined />
<FundOutlined />
<SlidersOutlined />
<AndroidOutlined />
<AppleOutlined />
<WindowsOutlined />
<IeOutlined />
<ChromeOutlined />
<GithubOutlined />
<AliwangwangOutlined />
<DingdingOutlined />
<WeiboSquareOutlined />
<WeiboCircleOutlined />
<TaobaoCircleOutlined />
<Html5Outlined />
<WeiboOutlined />
<TwitterOutlined />
<WechatOutlined />
<AlipayCircleOutlined />
<TaobaoOutlined />
<SkypeOutlined />
<FacebookOutlined />
<CodepenOutlined />
<CodeSandboxOutlined />
<AmazonOutlined />
<GoogleOutlined />
<AlipayOutlined />
<AntDesignOutlined />
<AntCloudOutlined />
<ZhihuOutlined />
<SlackOutlined />
<SlackSquareOutlined />
<BehanceSquareOutlined />
<DribbbleOutlined />
<DribbbleSquareOutlined />
<InstagramOutlined />
<YuqueOutlined />
<AlibabaOutlined />
<YahooOutlined />
<RedditOutlined />
<SketchOutlined />
<AccountBookOutlined />
<AlertOutlined />
<ApartmentOutlined />
<ApiOutlined />
<QqOutlined />
<MediumWorkmarkOutlined />
<GitlabOutlined />
<MediumOutlined />
<GooglePlusOutlined />
<AppstoreAddOutlined />
<AppstoreOutlined />
<AudioOutlined />
<AudioMutedOutlined />
<AuditOutlined />
<BankOutlined />
<BarcodeOutlined />
<BarsOutlined />
<BellOutlined />
<BlockOutlined />
<BookOutlined />
<BorderOutlined />
<BranchesOutlined />
<BuildOutlined />
<BulbOutlined />
<CalculatorOutlined />
<CalendarOutlined />
<CameraOutlined />
<CarOutlined />
<CarryOutOutlined />
<CiCircleOutlined />
<CiOutlined />
<CloudOutlined />
<ClearOutlined />
<ClusterOutlined />
<CodeOutlined />
<CoffeeOutlined />
<CompassOutlined />
<CompressOutlined />
<ContactsOutlined />
<ContainerOutlined />
<ControlOutlined />
<CopyrightCircleOutlined />
<CopyrightOutlined />
<CreditCardOutlined />
<CrownOutlined />
<CustomerServiceOutlined />
<DashboardOutlined />
<DatabaseOutlined />
<DeleteColumnOutlined />
<DeleteRowOutlined />
<DisconnectOutlined />
<DislikeOutlined />
<DollarCircleOutlined />
<DollarOutlined />
<DownloadOutlined />
<EllipsisOutlined />
<EnvironmentOutlined />
<EuroCircleOutlined />
<EuroOutlined />
<ExceptionOutlined />
<ExpandAltOutlined />
<ExpandOutlined />
<ExperimentOutlined />
<ExportOutlined />
<EyeOutlined />
<FieldBinaryOutlined />
<FieldNumberOutlined />
<FieldStringOutlined />
<DesktopOutlined />
<DingtalkOutlined />
<FileAddOutlined />
<FileDoneOutlined />
<FileExcelOutlined />
<FileExclamationOutlined />
<FileOutlined />
<FileImageOutlined />
<FileJpgOutlined />
<FileMarkdownOutlined />
<FilePdfOutlined />
<FilePptOutlined />
<FileProtectOutlined />
<FileSearchOutlined />
<FileSyncOutlined />
<FileTextOutlined />
<FileUnknownOutlined />
<FileWordOutlined />
<FilterOutlined />
<FireOutlined />
<FlagOutlined />
<FolderAddOutlined />
<FolderOutlined />
<FolderOpenOutlined />
<ForkOutlined />
<FormatPainterOutlined />
<FrownOutlined />
<FunctionOutlined />
<FunnelPlotOutlined />
<GatewayOutlined />
<GifOutlined />
<GiftOutlined />
<GlobalOutlined />
<GoldOutlined />
<GroupOutlined />
<HddOutlined />
<HeartOutlined />
<HistoryOutlined />
<HomeOutlined />
<HourglassOutlined />
<IdcardOutlined />
<ImportOutlined />
<InboxOutlined />
<InsertRowAboveOutlined />
<InsertRowBelowOutlined />
<InsertRowLeftOutlined />
<InsertRowRightOutlined />
<InsuranceOutlined />
<InteractionOutlined />
<KeyOutlined />
<LaptopOutlined />
<LayoutOutlined />
<LikeOutlined />
<LineOutlined />
<LinkOutlined />
<Loading3QuartersOutlined />
<LoadingOutlined />
<LockOutlined />
<MailOutlined />
<ManOutlined />
<MedicineBoxOutlined />
<MehOutlined />
<MenuOutlined />
<MergeCellsOutlined />
<MessageOutlined />
<MobileOutlined />
<MoneyCollectOutlined />
<MonitorOutlined />
<MoreOutlined />
<NodeCollapseOutlined />
<NodeExpandOutlined />
<NodeIndexOutlined />
<NotificationOutlined />
<NumberOutlined />
<PaperClipOutlined />
<PartitionOutlined />
<PayCircleOutlined />
<PercentageOutlined />
<PhoneOutlined />
<PictureOutlined />
<PoundCircleOutlined />
<PoundOutlined />
<PoweroffOutlined />
<PrinterOutlined />
<ProfileOutlined />
<ProjectOutlined />
<PropertySafetyOutlined />
<PullRequestOutlined />
<PushpinOutlined />
<QrcodeOutlined />
<ReadOutlined />
<ReconciliationOutlined />
<RedEnvelopeOutlined />
<ReloadOutlined />
<RestOutlined />
<RobotOutlined />
<RocketOutlined />
<SafetyCertificateOutlined />
<SafetyOutlined />
<ScanOutlined />
<ScheduleOutlined />
<SearchOutlined />
<SecurityScanOutlined />
<SelectOutlined />
<SendOutlined />
<SettingOutlined />
<ShakeOutlined />
<ShareAltOutlined />
<ShopOutlined />
<ShoppingCartOutlined />
<ShoppingOutlined />
<SisternodeOutlined />
<SkinOutlined />
<SmileOutlined />
<SolutionOutlined />
<SoundOutlined />
<SplitCellsOutlined />
<StarOutlined />
<SubnodeOutlined />
<SyncOutlined />
<TableOutlined />
<TabletOutlined />
<TagOutlined />
<TagsOutlined />
<TeamOutlined />
<ThunderboltOutlined />
<ToTopOutlined />
<ToolOutlined />
<TrademarkCircleOutlined />
<TrademarkOutlined />
<TransactionOutlined />
<TrophyOutlined />
<UngroupOutlined />
<UnlockOutlined />
<UploadOutlined />
<UsbOutlined />
<UserAddOutlined />
<UserDeleteOutlined />
<UserOutlined />
<UserSwitchOutlined />
<UsergroupAddOutlined />
<UsergroupDeleteOutlined />
<VideoCameraOutlined />
<WalletOutlined />
<WifiOutlined />
<BorderlessTableOutlined />
<WomanOutlined />
<BehanceOutlined />
<DropboxOutlined />
<DeploymentUnitOutlined />
<UpCircleOutlined />
<DownCircleOutlined />
<LeftCircleOutlined />
<RightCircleOutlined />
<UpSquareOutlined />
<DownSquareOutlined />
<LeftSquareOutlined />
<RightSquareOutlined />
<PlayCircleOutlined />
<QuestionCircleOutlined />
<PlusCircleOutlined />
<PlusSquareOutlined />
<MinusSquareOutlined />
<MinusCircleOutlined />
<InfoCircleOutlined />
<ExclamationCircleOutlined />
<CloseCircleOutlined />
<CloseSquareOutlined />
<CheckCircleOutlined />
<CheckSquareOutlined />
<ClockCircleOutlined />
<FormOutlined />
<DashOutlined />
<SmallDashOutlined />
<YoutubeOutlined />
<CodepenCircleOutlined />
<AliyunOutlined />
<PlusOutlined />
<LinkedinOutlined />
<AimOutlined />
<BugOutlined />
<CloudDownloadOutlined />
<CloudServerOutlined />
<CloudSyncOutlined />
<CloudUploadOutlined />
<CommentOutlined />
<ConsoleSqlOutlined />
<EyeInvisibleOutlined />
<FileGifOutlined />
<DeliveredProcedureOutlined />
<FieldTimeOutlined />
<FileZipOutlined />
<FolderViewOutlined />
<FundProjectionScreenOutlined />
<FundViewOutlined />
<MacCommandOutlined />
<PlaySquareOutlined />
<OneToOneOutlined />
<RotateLeftOutlined />
<RotateRightOutlined />
<SaveOutlined />
<SwitcherOutlined />
<TranslationOutlined />
<VerifiedOutlined />
<VideoCameraAddOutlined />
<WhatsAppOutlined />
{/*</Col>*/}
</Row>
);
}
Example #4
Source File: index.tsx From imove with MIT License | 4 votes |
MyConsole: React.FC = () => {
const [filter, setFilter] = useState('');
const [level, setLevel] = useState('all');
const [logList, setLogList] = useState<ILog[]>([]);
const cache = useRef<{ allLogs: ILog[] }>({ allLogs: [] });
useEffect(() => {
hijackConsole();
return () => {
resetConsole();
};
}, []);
useEffect(() => {
const filteredLogs = cache.current.allLogs
.filter((log) => {
if (level === 'all') {
return true;
} else {
return log.type === level;
}
})
.filter((log) => {
return log.strVal.indexOf(filter) > -1;
});
setLogList(filteredLogs);
}, [filter, level]);
const hijackConsole = () => {
Object.keys(hijackMap).forEach((method) => {
// @ts-ignore
window.console[method] = (...args: any[]) => {
hijackMap[method].originMethod(...args);
cache.current.allLogs = cache.current.allLogs.concat({
type: method,
data: args,
strVal: Helper.getArgsToString(args),
});
setLogList(cache.current.allLogs);
};
});
};
const resetConsole = () => {
Object.keys(hijackMap).forEach((method) => {
// @ts-ignore
window.console[method] = hijackMap[method].originMethod;
});
};
const onChangeFilter = useCallback((evt: ChangeEvent<HTMLInputElement>) => {
setFilter(evt.target.value);
}, []);
const onChangeLevel = useCallback((level: string) => {
setLevel(level);
}, []);
const onClickClear = useCallback(() => {
setLogList([]);
cache.current.allLogs = [];
}, []);
const renderToolBar = () => {
return (
<div className={styles.toolBar}>
<Input
allowClear={true}
placeholder={'过滤'}
prefix={<FilterOutlined />}
onChange={onChangeFilter}
/>
<Select
className={styles.levels}
defaultValue={'all'}
onChange={onChangeLevel}
>
{['all', 'info', 'warn', 'error', 'debug'].map((method) => (
<Select.Option key={method} value={method}>
{method}
</Select.Option>
))}
</Select>
<Button type={'link'} onClick={onClickClear}>
<ClearOutlined /> 清空
</Button>
</div>
);
};
const renderLogPanel = (logList: ILog[]) => {
return (
<div className={styles.logPanel}>
{logList.map((log: ILog, index: number) => (
<LogLine key={index} {...log} />
))}
</div>
);
};
return (
<div className={styles.container}>
<Tabs type={'card'} tabBarExtraContent={renderToolBar()}>
<Tabs.TabPane tab={'控制台'} key={'log'}>
{renderLogPanel(logList)}
</Tabs.TabPane>
</Tabs>
</div>
);
}
Example #5
Source File: monitor.tsx From leek with Apache License 2.0 | 4 votes |
MonitorPage = () => {
const service = new StatsService();
const { currentTheme } = useThemeSwitcher();
const [loading, setLoading] = useState<boolean>();
const [totalHits, setTotalHits] = useState<number>(0);
const [statesDistribution, setStatesDistribution] = useState<any>([]);
const [queuesDistribution, setQueuesDistribution] = useState<any>([]);
const [topExecutions, setTopExecutions] = useState<any>([]);
const [topSlow, setTopSlow] = useState<any>([]);
const [tasksOverTimeDistribution, setTasksOverTimeDistribution] =
useState<any>([]);
// Filters
const { currentApp, currentEnv } = useApplication();
const [filters, setFilters] = useState<any>();
const [timeDistributionTSType, setTimeDistributionTSType] =
useState<any>("timestamp");
const [timeFilters, setTimeFilters] = useState<any>({
timestamp_type: "timestamp",
interval_type: "past",
offset: 900000,
});
function handleFilterChange(values) {
setFilters(values);
}
useEffect(() => {
if (!currentApp) return;
let allFilters = {
...filters,
...timeFilters,
};
setLoading(true);
service
.charts(
currentApp,
currentEnv,
"desc",
allFilters,
timeDistributionTSType
)
.then(handleAPIResponse)
.then((result: any) => {
setStatesDistribution(result.aggregations.statesDistribution.buckets);
let totalInQueues = 0;
setQueuesDistribution(
result.aggregations.queuesDistribution.buckets.map(
({ key, doc_count }) => {
totalInQueues += doc_count;
return { id: key, label: key, value: doc_count };
}
)
);
let tasksDistribution =
result.aggregations.tasksDistribution.buckets.map(
({ key, statesDistribution, runtimeDistribution, doc_count }) => {
let tasksStatesSeries = {
QUEUED: 0,
RECEIVED: 0,
STARTED: 0,
SUCCEEDED: 0,
FAILED: 0,
REJECTED: 0,
REVOKED: 0,
RETRY: 0,
RECOVERED: 0,
CRITICAL: 0,
};
const states = statesDistribution.buckets.reduce(
(result, item) => {
result[item.key] = item.doc_count;
return result;
},
tasksStatesSeries
);
return {
id: key,
runtime: runtimeDistribution.value,
executions: doc_count,
...states,
};
}
);
setTopExecutions(tasksDistribution.slice(0, 5));
setTopSlow(
[...tasksDistribution]
.sort(function (a, b) {
return b.runtime - a.runtime;
})
.slice(0, 5)
.filter((task) => {
return task.runtime;
})
);
setTasksOverTimeDistribution([
{
id: "tasks",
data: result.aggregations.timeDistribution.buckets.map(
({ key, doc_count }) => ({
x: moment(key).format("YYYY-MM-DD HH:mm:ss"),
y: doc_count,
})
),
},
]);
setTotalHits(totalInQueues);
}, handleAPIError)
.catch(handleAPIError)
.finally(() => setLoading(false));
}, [currentApp, currentEnv, filters, timeFilters, timeDistributionTSType]);
return (
<>
<Helmet>
<html lang="en" />
<title>Monitor</title>
<meta name="description" content="Tasks monitor" />
<meta name="keywords" content="celery, monitor" />
</Helmet>
<Row style={{ marginBottom: "16px" }} gutter={[12, 12]}>
<Col xxl={5} xl={6} md={7} lg={8} sm={24} xs={24}>
<AttributesFilter
onFilter={handleFilterChange}
filters={timeFilters}
/>
</Col>
<Col xxl={19} xl={18} md={17} lg={16} sm={24} xs={24}>
<Card size="small">
<Row
align="middle"
style={{ textAlign: "center" }}
justify="space-between"
>
<Col>
<TimeFilter
timeFilter={timeFilters}
onTimeFilterChange={setTimeFilters}
/>
</Col>
<Col>
<Statistic
title="Total Filtered"
value={totalHits}
prefix={<FilterOutlined />}
/>
</Col>
</Row>
</Card>
<Row style={{ width: "100%", marginTop: 13 }} gutter={[10, 0]}>
<Col span={12}>
<Card
bodyStyle={{
paddingBottom: 0,
paddingRight: 0,
paddingLeft: 0,
}}
size="small"
style={{ width: "100%" }}
title="States distribution"
>
<Row style={{ height: "400px" }}>
<LeekPie data={statesDistribution} theme={currentTheme} />
</Row>
</Card>
</Col>
<Col span={12}>
<Card
bodyStyle={{
paddingBottom: 0,
paddingRight: 0,
paddingLeft: 0,
}}
size="small"
style={{ width: "100%" }}
title="Queues distribution"
>
<Row style={{ height: "400px" }}>
<LeekWaffle
total={totalHits}
data={queuesDistribution}
theme={currentTheme}
/>
</Row>
</Card>
</Col>
</Row>
<Row justify="center" style={{ width: "100%", marginTop: 13 }}>
<Card
bodyStyle={{ paddingBottom: 0, paddingRight: 0, paddingLeft: 0 }}
size="small"
style={{ width: "100%" }}
title="Top 5 Executed Tasks"
>
<Row style={{ height: "400px" }}>
<LeekBar
data={topExecutions}
keys={StatesKeys}
theme={currentTheme}
/>
</Row>
</Card>
</Row>
<Row justify="center" style={{ width: "100%", marginTop: 13 }}>
<Card
bodyStyle={{ paddingBottom: 0, paddingRight: 0, paddingLeft: 0 }}
size="small"
style={{ width: "100%" }}
title="Tasks over time distribution"
extra={[
<Select
key="timestamp"
defaultValue="timestamp"
dropdownMatchSelectWidth
style={{ width: 115 }}
size="small"
onChange={(type) => setTimeDistributionTSType(type)}
>
<Option value="timestamp">Seen</Option>
<Option value="queued_at">Queued</Option>
<Option value="received_at">Received</Option>
<Option value="started_at">Started</Option>
<Option value="succeeded_at">Succeeded</Option>
<Option value="failed_at">Failed</Option>
<Option value="retried_at">Retried</Option>
<Option value="rejected_at">Rejected</Option>
<Option value="revoked_at">Revoked</Option>
<Option value="eta">ETA</Option>
<Option value="expires">Expires</Option>
</Select>,
]}
>
<Row style={{ height: "400px" }}>
<LeekLine
data={tasksOverTimeDistribution}
theme={currentTheme}
/>
</Row>
</Card>
</Row>
<Row justify="center" style={{ width: "100%", marginTop: 13 }}>
<Card
bodyStyle={{ paddingBottom: 0, paddingRight: 0, paddingLeft: 0 }}
size="small"
style={{ width: "100%" }}
title={
<>
Top 5 Slow <TaskState state={"SUCCEEDED"} />
<TaskState state={"RECOVERED"} />
Tasks
</>
}
>
<Row style={{ height: "400px" }}>
<LeekVerticalBar
data={topSlow}
keys={["runtime"]}
color="yellow_orange_red"
theme={currentTheme}
/>
</Row>
</Card>
</Row>
</Col>
</Row>
</>
);
}
Example #6
Source File: BasicTable.tsx From fe-v5 with Apache License 2.0 | 4 votes |
// 泛型函数组件 https://juejin.im/post/5cd7f2c4e51d453a7d63b715#heading-7
function BasicTable<T>(props: IBasicTableProps<T>) {
const prefixCls = `${props.prefixCls || 'dantd'}-table`;
const filterType = props.filterType || 'half';
const tmpDataSource = props.dataSource || ([] as T[]);
const t = intlZhMap;
const [dataSource, setDataSource] = useState(tmpDataSource);
const [queryFormValues, setQueryFormValues] = useState<any>({});
const [isQueryInit, setQueryInit] = useState(false);
const {
showFilter = true,
showSearch = true,
showReloadBtn = true,
showQueryOptionBtns = true,
showQueryCollapseButton = true,
queryFormProps = {},
isQuerySearchOnChange = true,
loading = false,
showBodyBg = false,
queryFormColumns,
queryMode = 'default',
searchPos = 'full',
reloadBtnPos = 'right',
reloadBtnType = 'icon',
antProps = {
rowKey: 'id',
},
} = props;
// 整理搜索区的展示状态
// 是否展示长条搜索区
const showFullSearch = showSearch && searchPos === 'full';
// 搜索按钮展示的位置
const showReloadBtn2SearchRight =
searchPos === 'full' && showReloadBtn && reloadBtnPos === 'right';
const showReloadBtn2FilterRight =
(!showSearch || searchPos !== 'full') &&
showReloadBtn &&
reloadBtnPos === 'right';
const showReloadBtn2FilterLeft = showReloadBtn && reloadBtnPos === 'left';
const searchPlaceholder =
props.searchPlaceholder || t('table.search.placeholder');
const tableClassName = classNames(prefixCls, props.className);
const tableContentClassName = classNames({
[`${prefixCls}-table-content`]: true,
[`${prefixCls}-table-content-noborder`]: props.hideContentBorder,
});
const tableBodyCls = classNames({
[`${prefixCls}-body`]: !!queryFormColumns || showBodyBg,
[`${prefixCls}-body-compact`]: queryMode === 'compact',
});
const tableQueryCls = classNames({
[`${prefixCls}-query`]: !!queryFormColumns,
[`${prefixCls}-query-compact`]: queryMode === 'compact',
});
const filterSearchInputRef = useRef({}) as any;
const clearFiltersRef = useRef({}) as any;
const [searchQuery, setSearchQuery] = useState('');
const [showRightHeader, setShowRightHeader] = useState<boolean>(false);
const [sorterState, sorterDispatch] = useReducer(sorterReducer, {});
const rowSelection = props.rowSelection;
const selectRowNum = rowSelection
? rowSelection.selectedRowKeys && rowSelection.selectedRowKeys.length
: -1;
const sorterNames = {
ascend: t('table.sort.ascend'),
descend: t('table.sort.descend'),
};
const showTotal = (total: number) => {
return `${t('table.total.prefix')} ${total} ${t('table.total.suffix')}`;
};
// 生成列的 dataIndex 数组
const columnsDataIndexArr =
props.columns.map((columnItem) => {
let dataIndex = columnItem.dataIndex;
if (typeof dataIndex === 'object' && dataIndex !== null) {
// metadata name
dataIndex = dataIndex.join(' ');
}
return dataIndex;
}) || [];
const renderCurTarget = (
data: Object,
parentKey: Array<String>,
curTarget = '',
) => {
let returnItem = '';
Object.entries(data).forEach(([curKey, curItem]) => {
// 如果属性的值是对象,继续递归
if (typeof curItem === 'object' && curItem !== null) {
returnItem += ' ' + renderCurTarget(curItem, parentKey.concat(curKey));
}
columnsDataIndexArr.some((dataIndex) => {
// 如果data对象的属性匹配到了columnsDataIndexArr中的某一项
if (dataIndex === parentKey.concat(curKey).join(' ')) {
// 当值为String|Number|Array类型时,将值加到curTarget中
if (
typeof curItem === 'string' ||
typeof curItem === 'number' ||
Array.isArray(curItem)
) {
returnItem += ' ' + curItem;
}
}
});
});
return `${curTarget} ${returnItem}`;
};
const dataSourceMap = useMemo(() => {
if (!props.dataSource || !Array.isArray(props.dataSource)) {
return [];
}
return props.dataSource.reduce((acc, curVal, curIdx) => {
// let curTarget = ''
// Object.entries(curVal).forEach(([curKey, curItem]) => {
// if (columnsDataIndexArr.indexOf(curKey) >= 0 && (typeof curItem === 'string' || typeof curItem === 'number')) {
// curTarget = `${curTarget} ${curItem}`
// }
// })
return {
...acc,
[curIdx]: renderCurTarget(curVal, []).toLowerCase(),
};
}, {});
}, [columnsDataIndexArr, props.dataSource]);
// console.log('basicTable dataSourceMap', dataSourceMap)
const columnsMap = useMemo(() => {
const result = {} as any;
if (props.columns && props.columns.length > 0) {
props.columns.forEach((columnItem) => {
if (columnItem.dataIndex) {
result[columnItem.dataIndex as string] = {
title: columnItem.title,
dataIndex: columnItem.dataIndex,
value: null,
commonSearch: columnItem.commonSearch,
commonFilter: columnItem.commonFilter,
commonSorter: columnItem.commonSorter,
};
}
});
}
return result;
}, [props.columns]);
// console.log('basicTable columnsMap', columnsMap)
function columnInit(initColumnState) {
return initColumnState;
}
function columnsReducer(
state: IColumnsReducerState,
action: TColumnsReducerAction,
) {
switch (action.type) {
case 'update':
return {
...state,
[action.dataIndex]: {
...state[action.dataIndex],
dataIndex: action.dataIndex,
type: action.updateType,
value: action.value,
},
};
case 'clear':
return columnInit(action.initialState);
default:
return state;
}
}
const [columnsState, columnsDispatch] = useReducer(
columnsReducer,
columnsMap,
columnInit,
);
const debouncedSearchQuery = useDebounce(searchQuery, 300);
useDeepCompareEffect(() => {
// 模糊匹配
let newDataSource = [...tmpDataSource];
if (showSearch && debouncedSearchQuery && debouncedSearchQuery.trim()) {
if (props.onSearch) {
// 使用用户自定义的search回调
props.onSearch(debouncedSearchQuery.trim());
return;
} else {
// 使用组件默认的search回调
const debouncedSearchArr = debouncedSearchQuery
.trim()
.toLowerCase()
.split(' ');
newDataSource = newDataSource.filter((fiterItem, filterIdx) => {
const filterStr = dataSourceMap[filterIdx];
return debouncedSearchArr.every(
(someItem) => filterStr.indexOf(someItem) >= 0,
);
});
}
}
if (queryFormColumns && Object.keys(queryFormValues).length > 0) {
newDataSource = _.filter(newDataSource, (sourceItem) => {
return Object.entries(queryFormValues).every(
([queryKey, queryValue]) => {
if (
(!queryValue && queryValue !== 0) ||
(Array.isArray(queryValue) && queryValue.length === 0)
) {
return true;
}
if (typeof queryValue === 'string') {
return (
sourceItem[queryKey] &&
sourceItem[queryKey].indexOf(queryValue) >= 0
);
}
if (typeof queryValue === 'number') {
return sourceItem[queryKey] === queryValue;
}
if (Array.isArray(queryValue) && queryValue.length > 0) {
return queryValue.indexOf(sourceItem[queryKey]) >= 0;
}
},
);
});
}
setDataSource(newDataSource);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedSearchQuery, queryFormValues, props.dataSource]);
const handleChange = (
pagination: any,
filters: any,
sorter: any,
extra: any,
) => {
sorterDispatch({
type: 'update',
value: sorter,
});
Object.entries(filters).forEach(([filterKey, filterValue]) => {
if (columnsMap[filterKey].commonFilter) {
columnsDispatch({
type: 'update',
dataIndex: filterKey,
updateType: 'filter',
value: filterValue,
});
}
});
checkRightHeader(filters, sorter);
if (props.onChange) {
props.onChange(pagination, filters, sorter, extra);
}
};
const checkRightHeader = (filters?: any, sorter?: any, search?: any) => {
const checkSorter = sorter || sorterState;
const checkFilters = filters || columnsState;
const checkSearch = search || columnsState;
const isSearch = Object.values(checkSearch).some((columnItem: any) => {
return !!(columnItem.type === 'search' && columnItem.value);
});
let isFilter = false;
if (filters) {
isFilter = Object.values(checkFilters).some((columnItem: any) => {
return columnItem && columnItem.length > 0;
});
} else {
isFilter = Object.values(checkFilters).some((columnItem: any) => {
return !!(
columnItem.type === 'filter' &&
columnItem.value &&
columnItem.value.length > 0
);
});
}
const isSorter = !!checkSorter.column;
const res = isSearch || isFilter || isSorter;
setShowRightHeader(res);
};
const handleFilterSearch = useCallback(
(
selectedKeys: React.ReactText[] | undefined,
confirm: (() => void) | undefined,
dataIndex: string | number,
) => {
if (confirm) {
confirm();
}
if (selectedKeys && dataIndex) {
columnsDispatch({
type: 'update',
dataIndex,
updateType: 'search',
value: selectedKeys[0],
});
}
},
[],
);
const handleFilterSearchReset = useCallback(
(
clearFilters: ((selectedKeys: string[]) => void) | undefined,
dataIndex: string | number | undefined,
) => {
if (clearFilters) {
clearFilters([]);
}
if (dataIndex) {
columnsDispatch({
type: 'update',
dataIndex,
});
}
},
[],
);
const handleClearAll = () => {
sorterDispatch({ type: 'clear' });
columnsDispatch({ type: 'clear', initialState: columnsMap });
setShowRightHeader(false);
setTimeout(() => {
Object.values(clearFiltersRef.current).forEach((filterItem: any) => {
if (filterItem && filterItem.clearFilters) {
filterItem.clearFilters([]);
}
});
});
};
const handleSortClear = () => {
sorterDispatch({ type: 'clear' });
checkRightHeader(null, {});
};
const handleFilterClear = (columnValue: IColumnsReducerValue) => {
columnsDispatch({
type: 'update',
dataIndex: columnValue.dataIndex,
value: [],
});
const tmpFilters = Object.values(columnsState).map((filterItem: any) => {
if (filterItem.dataIndex === columnValue.dataIndex) {
return {
[filterItem.dataIndex]: [],
};
}
return {
[filterItem.dataIndex]: filterItem.value || [],
};
});
checkRightHeader(tmpFilters, sorterState, columnsState);
};
const handleFilterSearchClear = (columnValue: IColumnsReducerValue) => {
columnsDispatch({
type: 'update',
dataIndex: columnValue.dataIndex,
});
if (
clearFiltersRef.current[columnValue.dataIndex] &&
clearFiltersRef.current[columnValue.dataIndex].clearFilters
) {
clearFiltersRef.current[columnValue.dataIndex].clearFilters([]);
}
checkRightHeader(null, sorterState, {
...columnsState,
[columnValue.dataIndex]: {
title: columnsState[columnValue.dataIndex].title,
dataIndex: columnsState[columnValue.dataIndex].dataIndex,
value: null,
},
});
};
const renderColumns = () => {
// if (!dataSource || (dataSource && dataSource.length === 0)) {
// return props.columns;
// }
const handledColumns = props.columns.map((columnItem) => {
const currentItem = _.cloneDeep(columnItem);
// filter
if (currentItem.commonFilter && !currentItem.filters) {
const filters = _.uniq(_.map(dataSource, columnItem.dataIndex));
currentItem.filters = filters.map((value: string) => {
return {
text: value,
value,
};
});
currentItem.filterIcon = () => <FilterOutlined />;
currentItem.filteredValue =
columnsState[columnItem.dataIndex as string].value;
currentItem.onFilter = (value, record: any) => {
if (currentItem.dataIndex && record[currentItem.dataIndex]) {
return record[currentItem.dataIndex] === value;
}
return false;
};
}
// sort
if (currentItem.commonSorter) {
currentItem.sorter = (aItem: any, bItem: any) => {
const a = aItem[currentItem.dataIndex as string];
const b = bItem[currentItem.dataIndex as string];
// number
const numA = Number(a);
const numB = Number(b);
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB;
}
// date
const dateA = +new Date(a);
const dateB = +new Date(b);
if (!isNaN(dateA) && !isNaN(dateB)) {
return dateA - dateB;
}
// string
if (typeof a === 'string' && typeof b === 'string') {
return a > b ? 1 : -1;
}
return 0;
};
currentItem.sortOrder =
sorterState.columnKey === currentItem.dataIndex && sorterState.order;
}
// Search
if (currentItem.commonSearch) {
currentItem.filterIcon = () => <SearchOutlined />;
currentItem.onFilterDropdownVisibleChange = (visible: boolean) => {
if (
visible &&
filterSearchInputRef.current &&
filterSearchInputRef.current.select
) {
setTimeout(() => {
if (
filterSearchInputRef.current &&
filterSearchInputRef.current.select
) {
filterSearchInputRef.current.select();
}
return null;
});
}
};
currentItem.filterDropdown = ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
clearFiltersRef.current[currentItem.dataIndex as string] = {
clearFilters,
};
return (
<div style={{ padding: 8 }}>
<Input
data-testid='filter-search-input'
autoFocus={true}
ref={(node) => {
filterSearchInputRef.current = node;
}}
placeholder={t('table.filter.search.placeholder')}
value={selectedKeys && selectedKeys[0]}
onChange={(e) => {
if (setSelectedKeys) {
return setSelectedKeys(
e.target.value ? [e.target.value] : [],
);
}
return [];
}}
onPressEnter={() =>
handleFilterSearch(
selectedKeys,
confirm,
currentItem.dataIndex as string,
)
}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Button
type='primary'
onClick={() =>
handleFilterSearch(
selectedKeys,
confirm,
currentItem.dataIndex as string,
)
}
icon='search'
size='small'
data-testid='search-btn-ok'
style={{ width: 90, marginRight: 8 }}
>
{t('table.filter.search.btn.ok')}
</Button>
<Button
onClick={() =>
handleFilterSearchReset(clearFilters, currentItem.dataIndex)
}
size='small'
style={{ width: 90 }}
>
{t('table.filter.search.btn.cancel')}
</Button>
</div>
);
};
let searchWords: any[] = [];
const tmpStateValue =
columnsState[currentItem.dataIndex as string].value;
if (typeof tmpStateValue === 'string') {
searchWords = [tmpStateValue];
}
if (
Array.isArray(tmpStateValue) &&
typeof tmpStateValue[0] === 'string'
) {
searchWords = [tmpStateValue[0]];
}
currentItem.onFilter = (value, record: any) => {
return record[currentItem.dataIndex as string]
.toString()
.toLowerCase()
.includes(value.toLowerCase());
};
if (!currentItem.render) {
currentItem.render = (value, row, index) => {
if (currentItem.searchRender) {
return currentItem.searchRender(
value,
row,
index,
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={searchWords}
autoEscape
textToHighlight={String(value)}
/>,
);
} else {
return (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={searchWords}
autoEscape
textToHighlight={String(value)}
/>
);
}
};
}
}
return currentItem;
});
// console.log('basicTable handledColumns', handledColumns)
return handledColumns;
};
const isSearch = Object.values(columnsState).some((columnItem: any) => {
return !!(columnItem.type === 'search' && columnItem.value);
});
const isFilter = Object.values(columnsState).some((columnItem: any) => {
return !!(
columnItem.type === 'filter' &&
columnItem.value &&
columnItem.value.length > 0
);
});
const handleReload = () => {
if (props.onReload) {
props.onReload();
}
};
const handleSearchChange = (e) => {
const query = e.target.value;
setSearchQuery(query);
};
const handleQueryFormInit = (data) => {
if (!isQueryInit) {
setQueryFormValues(data);
setQueryInit(true);
}
};
const handleQueryFormChange = (data) => {
setQueryFormValues(data);
};
const handleQueryFormReset = (data) => {
setQueryFormValues(data);
};
const renderRightHeader = (params) => {
if (!showFilter) {
return null;
}
return (
<>
<div>
<b
style={{
display: 'inline-block',
marginTop: 3,
}}
>
{t('table.filter.header.title')}
</b>
</div>
{(isSearch || isFilter) &&
Object.values(columnsState as IColumnsReducerState).map(
(columnValue) => {
if (columnValue.type === 'search' && columnValue.value) {
return (
<div
key={`search-header-${columnValue.dataIndex}`}
className={`${params.headerClsPrefix}-item`}
>
<Tooltip
title={
<span>
{columnValue.title}
{':'}
{columnValue.value}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={() => handleFilterSearchClear(columnValue)}
>
<span className='table-header-item-btn-content'>
{columnValue.title}
{':'}
{columnValue.value}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
);
}
if (
columnValue.type === 'filter' &&
columnValue.value &&
columnValue.value.length > 0
) {
return (
<div
key={`search-header-${columnValue.dataIndex}`}
className={`${params.headerClsPrefix}-item`}
>
<Tooltip
title={
<span>
{columnValue.title}
{':'}
{columnValue.value.join(',')}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={() => handleFilterClear(columnValue)}
>
<span className='table-header-item-btn-content'>
{columnValue.title}
{':'}
{columnValue.value.join(',')}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
);
}
return null;
},
)}
{sorterState.columnKey && sorterState.column && (
<div className={`${params.headerClsPrefix}-item`}>
<Tooltip
title={
<span>
{sorterState.column.title}
{`: ${sorterNames[sorterState.order as TSorterNames]}`}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={handleSortClear}
>
<span className='table-header-item-btn-content'>
{sorterState.column.title}
{`: ${sorterNames[sorterState.order as TSorterNames]}`}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
)}
<div className={`${params.headerClsPrefix}-item`}>
<Button
size='small'
type='link'
data-testid='btn-clearall'
onClick={handleClearAll}
>
{t('table.filter.header.btn.clear')}
</Button>
</div>
</>
);
};
const renderSearch = () => {
return (
<Tooltip placement='topLeft' title={searchPlaceholder}>
<Input
data-testid='search-input'
prefix={
reloadBtnPos === 'right' && (
<SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />
)
}
suffix={
reloadBtnPos === 'left' && (
<SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />
)
}
allowClear={true}
value={searchQuery}
onChange={handleSearchChange}
placeholder={searchPlaceholder}
/>
</Tooltip>
);
};
const renderReloadBtn = () => {
if (reloadBtnType === 'icon') {
const reloadBtnCls = classNames({
[`${prefixCls}-header-loadbtn`]: true,
[`${prefixCls}-header-loadbtn-icon`]: true,
[`${prefixCls}-header-loadbtn-right`]: reloadBtnPos === 'right',
[`${prefixCls}-header-loadbtn-left`]: reloadBtnPos === 'left',
});
return (
<ReloadOutlined
onClick={handleReload}
spin={loading}
className={reloadBtnCls}
/>
);
}
if (reloadBtnType === 'btn') {
const reloadBtnCls = classNames({
[`${prefixCls}-header-loadbtn`]: true,
[`${prefixCls}-header-loadbtn-btn`]: true,
[`${prefixCls}-header-loadbtn-right`]: reloadBtnPos === 'right',
[`${prefixCls}-header-loadbtn-left`]: reloadBtnPos === 'left',
});
return (
<Button
className={reloadBtnCls}
loading={loading}
icon={<ReloadOutlined />}
data-testid='reload-btn'
onClick={handleReload}
/>
);
}
};
return (
<ConfigProvider {...props.antConfig}>
<div className={tableClassName} style={props.style}>
{queryFormColumns && (
<div className={tableQueryCls}>
<QueryForm
onChange={
isQuerySearchOnChange
? handleQueryFormChange
: handleQueryFormInit
}
onReset={handleQueryFormReset}
onSearch={handleQueryFormChange}
columns={queryFormColumns}
showOptionBtns={showQueryOptionBtns}
showCollapseButton={showQueryCollapseButton}
{...queryFormProps}
/>
</div>
)}
<div className={tableBodyCls}>
{!!props.tableTitle && <h3> {props.tableTitle} </h3>}
{showFullSearch && (
<Row className={`${prefixCls}-header-search`}>
{showSearch ? renderSearch() : <div></div>}
{showReloadBtn2SearchRight && renderReloadBtn()}
</Row>
)}
{/* 自定义格式 */}
{filterType === 'flex' && (
<div
style={{
display: 'flex',
justifyContent: 'flex-start',
marginBottom: '10px',
}}
>
{showReloadBtn2FilterLeft && renderReloadBtn()}
{props.leftHeader}
</div>
)}
{filterType === 'none' && searchPos !== 'right' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
>
{props.leftHeader}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'none' && showSearch && searchPos === 'right' && (
<Row className={`${prefixCls}-header-filter`} align='middle'>
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
span={13}
>
{showReloadBtn2FilterLeft && renderReloadBtn()}
{props.leftHeader}
</Col>
<Col
span={6}
data-testid='right-header'
className={`${prefixCls}-header-filter-right`}
>
{props.customHeader && (
<div data-testid='custom-header'>{props.customHeader}</div>
)}
</Col>
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-right`}
span={5}
>
{renderSearch()}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'line' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-line`}
span={24}
>
{showRightHeader &&
renderRightHeader({
headerClsPrefix: `${prefixCls}-header-filter-line`,
})}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'half' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
span={12}
>
{props.leftHeader}
</Col>
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-right`}
span={12}
>
{showRightHeader &&
renderRightHeader({
headerClsPrefix: `${prefixCls}-header-filter-right`,
})}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
<div className={`${prefixCls}-table-wrapper`}>
<div className={tableContentClassName}>
<Table
loading={loading}
columns={renderColumns()}
dataSource={dataSource}
bordered={false}
rowSelection={rowSelection}
onChange={handleChange}
pagination={{
showTotal: showTotal,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: pageSizeOptions,
...props.pagination,
}}
{...antProps}
/>
{rowSelection && selectRowNum !== undefined && selectRowNum > 0 && (
<div className={`${prefixCls}-table-content-select-num`}>
{t('table.select.num')}
{selectRowNum}
</div>
)}
</div>
</div>
</div>
</div>
</ConfigProvider>
);
}
Example #7
Source File: DataTable.tsx From fe-v5 with Apache License 2.0 | 4 votes |
function DataTable<T>(props: IDataTableProps<T>, ref: any) {
const prefixCls = `${props.prefixCls || 'dantd'}-data-table`;
const filterType = props.filterType || 'half';
const isUrlLoad = useRef<string>();
const t = intlZhMap;
const {
fetchOptions = {},
searchParams,
showFilter = true,
showReloadBtn = true,
showQueryOptionBtns = true,
showQueryCollapseButton = true,
isQuerySearchOnChange = true,
showBodyBg = false,
queryFormProps = {},
url,
queryMode = 'default',
reloadBtnPos = 'right',
searchPos = 'full',
reloadBtnType = 'icon',
queryFormColumns,
pageParams,
antProps = {
rowKey: 'id',
},
} = props;
const tableClassName = classNames(prefixCls, props.className);
const tableContentClassName = classNames({
[`${prefixCls}-table-content`]: true,
[`${prefixCls}-table-content-noborder`]: props.hideContentBorder,
});
const tableBodyCls = classNames({
[`${prefixCls}-body`]: !!queryFormColumns || showBodyBg,
[`${prefixCls}-body-compact`]: queryMode === 'compact',
});
const tableQueryCls = classNames({
[`${prefixCls}-query`]: !!queryFormColumns,
[`${prefixCls}-query-compact`]: queryMode === 'compact',
});
// hooks
const columnsMap = useMemo(() => {
const result = {} as any;
if (props.columns && props.columns.length > 0) {
props.columns.forEach((columnItem) => {
if (columnItem.dataIndex) {
result[columnItem.dataIndex as string] = {
...columnItem,
value: null,
};
}
});
}
return result;
}, [props.columns]);
const [queryFormValues, setQueryFormValues] = useState<any>({});
const [isQueryInit, setQueryInit] = useState(false);
const isPageChangeNoSearch = useRef<boolean>(false);
const filterSearchInputRef = useRef({}) as any;
const clearFiltersRef = useRef({}) as any;
const [dataSource, setDataSource] = useState([]);
const [loading, setLoading] = useState(false);
const [sorterState, sorterDispatch] = useReducer(sorterReducer, {});
const [showRightHeader, setShowRightHeader] = useState<boolean>(false);
const [searchQuery, setSearchQuery] = useState();
const rowSelection = props.rowSelection;
const selectRowNum = rowSelection
? rowSelection.selectedRowKeys && rowSelection.selectedRowKeys.length
: -1;
const sorterNames = {
ascend: t('table.sort.ascend'),
descend: t('table.sort.descend'),
};
const showTotal = (total: number) => {
return `${t('table.total.prefix')} ${total} ${t('table.total.suffix')}`;
};
const showSearch = !!searchParams;
// 是否展示长条搜索区
const showFullSearch = showSearch && searchPos === 'full';
// 搜索按钮展示的位置
const showReloadBtn2SearchRight =
searchPos === 'full' && showReloadBtn && reloadBtnPos === 'right';
const showReloadBtn2FilterRight =
(!showSearch || searchPos !== 'full') &&
showReloadBtn &&
reloadBtnPos === 'right';
const showReloadBtn2FilterLeft = showReloadBtn && reloadBtnPos === 'left';
const searchPlaceholder = searchParams
? searchParams.placeholder
: t('table.search.placeholder');
const [paginationState, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
...pageParams,
});
const [columnsState, columnsDispatch] = useReducer(
columnsReducer,
columnsMap,
);
const debouncedSearchQuery = useDebounce(searchQuery, 300);
const debouncedQueryFormValues = useDebounce(
JSON.stringify(queryFormValues),
300,
);
const pageStr = JSON.stringify({
pageSize: paginationState.pageSize,
current: paginationState.current,
});
const tableStateStr = JSON.stringify({
...columnsState,
...sorterState,
});
// TODO 支持监听 customQuery
useEffect(() => {
if (url && isUrlLoad.current !== url) {
let fetchParams = getAllFetchParams();
fetchData(fetchParams);
setTimeout(() => {
isUrlLoad.current = url;
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);
useEffect(() => {
if (!isUrlLoad.current) {
return;
}
let fetchParams = getAllFetchParams();
fetchParams[paginationState.curPageName] = 1;
if (searchParams && debouncedSearchQuery && debouncedSearchQuery.trim()) {
fetchParams[searchParams.apiName] = debouncedSearchQuery.trim();
}
if (props.onSearch) {
// 使用用户自定义的search回调
props.onSearch(
debouncedSearchQuery
? debouncedSearchQuery.trim()
: debouncedSearchQuery,
);
}
fetchData(fetchParams);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedSearchQuery]);
// 监听queryform
useEffect(() => {
if (!isUrlLoad.current) {
return;
}
if (
!queryFormColumns ||
(queryFormValues && Object.keys(queryFormValues).length === 0)
) {
return;
}
let fetchParams = getAllFetchParams();
fetchParams[paginationState.curPageName] = 1;
fetchData(fetchParams);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedQueryFormValues]);
useEffect(() => {
if (!isUrlLoad.current) {
return;
}
let fetchParams = getAllFetchParams();
fetchData(fetchParams);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tableStateStr]);
useEffect(() => {
if (!isUrlLoad.current || !paginationState.pageSize) {
return;
}
if (isPageChangeNoSearch.current) {
isPageChangeNoSearch.current = false;
return;
}
let fetchParams = getAllFetchParams();
fetchData(fetchParams);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageStr]);
async function fetchData(params?: any) {
let fetchUrl = url;
let customFetchParams = null as any;
if (fetchUrl.indexOf('?') !== -1) {
const fetchArr = fetchUrl.split('?');
fetchUrl = fetchArr[0];
customFetchParams = queryString.parse(fetchArr[1], {
arrayFormat: 'comma',
});
}
let fetchParams = {
[pageParams.curPageName]: params.current,
[pageParams.pageSizeName]: params.pageSize,
};
if (customFetchParams) {
fetchParams = {
...customFetchParams,
...fetchParams,
};
}
// api起始页从0开始,参数需要减1
if (pageParams.startPage === 0) {
fetchParams[pageParams.curPageName] -= 1;
}
props.columns.forEach((columnItem) => {
if (columnItem.dataIndex && params[columnItem.dataIndex]) {
fetchParams[columnItem.dataIndex] = params[columnItem.dataIndex];
}
if (columnItem.apiSearch && params[columnItem.apiSearch.name]) {
fetchParams[columnItem.apiSearch.name] =
params[columnItem.apiSearch.name];
}
if (columnItem.apiFilter && params[columnItem.apiFilter.name]) {
fetchParams[columnItem.apiFilter.name] =
params[columnItem.apiFilter.name];
}
if (columnItem.apiSorter && params[columnItem.apiSorter.name]) {
fetchParams[columnItem.apiSorter.name] =
params[columnItem.apiSorter.name];
}
});
if (searchParams && params[searchParams.apiName]) {
fetchParams[searchParams.apiName] = params[searchParams.apiName];
}
if (
!!queryFormColumns &&
queryFormValues &&
Object.keys(queryFormValues).length > 0
) {
fetchParams = {
...queryFormValues,
...fetchParams,
};
}
if (props.customQueryCallback) {
fetchParams = props.customQueryCallback({
...fetchParams,
});
}
fetchUrl = `${fetchUrl}?${queryString.stringify(fetchParams, {
arrayFormat: 'comma',
})}`;
if (props.customFetchUrlCallback) {
fetchUrl = props.customFetchUrlCallback(fetchUrl);
}
setLoading(true);
let transportOptions = {
...fetchOptions,
};
if (fetchOptions.method && fetchOptions.method.toLowerCase() === 'post') {
transportOptions.data = fetchParams;
}
const res = request(fetchUrl, transportOptions);
props.rowSelection &&
props.rowSelection.onChange &&
props.rowSelection.onChange([]);
if ((res.status < 200 || res.status >= 300) && props.apiErrorCallback) {
props.apiErrorCallback(res);
setLoading(false);
} else {
res
.then(async (res) => {
let callbackData = res;
if (props.apiCallback) {
callbackData = props.apiCallback(res);
}
if (props.apiAsyncCallback) {
callbackData = await props.apiAsyncCallback(res);
}
setDataSource(callbackData.data);
const tmpPagination = {
...paginationState,
total: callbackData.total,
};
setPagination(tmpPagination);
setLoading(false);
})
.catch(() => {
if (props.apiErrorCallback) {
props.apiErrorCallback(res);
}
setLoading(false);
});
}
}
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
if (!isUrlLoad.current) {
return;
}
if (Object.keys(sorter).length > 0 && sorter.column) {
sorterDispatch({
type: 'update',
value: {
...sorter,
column: {
apiSorter: sorter.column.apiSorter,
},
},
});
} else {
sorterDispatch({
type: 'update',
value: {},
});
}
Object.entries(filters).forEach(([filterKey, filterValue]) => {
if (columnsMap[filterKey].filters) {
columnsDispatch({
type: 'update',
dataIndex: filterKey,
updateType: 'filter',
value: filterValue,
});
}
});
isPageChangeNoSearch.current = false;
setPagination(pagination);
checkRightHeader(filters, sorter);
};
const handleFilterSearch = useCallback(
(
selectedKeys: React.ReactText[] | undefined,
confirm: (() => void) | undefined,
dataIndex: string | number,
) => {
if (confirm) {
confirm();
}
if (selectedKeys && dataIndex) {
columnsDispatch({
type: 'update',
dataIndex,
updateType: 'search',
value: selectedKeys[0],
});
}
},
[],
);
const handleFilterSearchReset = useCallback(
(
clearFilters: ((selectedKeys: string[]) => void) | undefined,
dataIndex: string | number | undefined,
) => {
if (clearFilters) {
clearFilters([]);
}
if (dataIndex) {
columnsDispatch({
type: 'update',
dataIndex,
});
}
},
[],
);
const handleFilterClear = (columnValue: IColumnsReducerValue) => {
columnsDispatch({
type: 'update',
dataIndex: columnValue.dataIndex,
value: [],
});
const tmpFilters = Object.values(columnsState).map((filterItem: any) => {
if (filterItem.dataIndex === columnValue.dataIndex) {
return {
[filterItem.dataIndex]: [],
};
}
return {
[filterItem.dataIndex]: filterItem.value || [],
};
});
checkRightHeader(tmpFilters, sorterState, columnsState);
};
const handleFilterSearchClear = (columnValue: IColumnsReducerValue) => {
columnsDispatch({
type: 'update',
dataIndex: columnValue.dataIndex,
});
if (
clearFiltersRef.current[columnValue.dataIndex] &&
clearFiltersRef.current[columnValue.dataIndex].clearFilters
) {
clearFiltersRef.current[columnValue.dataIndex].clearFilters([]);
}
checkRightHeader(null, sorterState, {
...columnsState,
[columnValue.dataIndex]: {
title: columnsState[columnValue.dataIndex].title,
dataIndex: columnsState[columnValue.dataIndex].dataIndex,
value: null,
},
});
};
const handleClearAll = () => {
sorterDispatch({ type: 'clear' });
columnsDispatch({ type: 'clear', initialState: columnsMap });
setShowRightHeader(false);
setTimeout(() => {
Object.values(clearFiltersRef.current).forEach((filterItem: any) => {
if (filterItem && filterItem.clearFilters) {
filterItem.clearFilters([]);
}
});
});
};
const handleSortClear = () => {
sorterDispatch({ type: 'clear' });
checkRightHeader(null, {});
};
const handleQueryFormInit = (data) => {
if (!isQueryInit) {
setQueryFormValues(data);
setQueryInit(true);
}
};
const handleQueryFormChange = (data) => {
isPageChangeNoSearch.current = true;
setPagination({
...paginationState,
current: 1,
});
setQueryFormValues(data);
};
const handleQueryFormReset = (data) => {
isPageChangeNoSearch.current = true;
setPagination({
...paginationState,
current: 1,
});
setQueryFormValues(data);
};
const handleQuerySearch = (data) => {
if (paginationState.current > 1) {
// 这次修改分页参数
isPageChangeNoSearch.current = true;
setPagination({
...paginationState,
current: 1,
});
setQueryFormValues(data);
} else {
setQueryFormValues(data);
// let fetchParams = getAllFetchParams();
// fetchData(fetchParams);
}
};
const getAllFetchParams = () => {
let fetchParams = {
...paginationState,
};
// columns sort
if (sorterState && sorterState.order) {
fetchParams[sorterState.column.apiSorter.name] =
sorterState.column.apiSorter[sorterState.order];
}
Object.values(columnsState).forEach((column: any) => {
const filterKey = column.dataIndex;
const filterVal = column.value;
// columns filter
if (column.apiFilter && filterVal) {
let filterName = columnsMap[filterKey].apiFilter
? columnsMap[filterKey].apiFilter.name
: filterKey;
fetchParams[filterName] = filterVal;
if (column.apiFilter.callback) {
fetchParams[filterName] =
columnsMap[filterKey].apiFilter.callback(filterVal);
}
}
// columns search
if (column.apiSearch && filterVal) {
const filterName = columnsMap[filterKey].apiSearch
? columnsMap[filterKey].apiSearch.name
: filterKey;
fetchParams[filterName] = Array.isArray(filterVal)
? filterVal[0]
: filterVal;
}
});
// query search
if (searchParams && searchQuery) {
fetchParams[searchParams.apiName] = searchQuery;
}
// queryform
if (
queryFormColumns ||
(queryFormValues && Object.keys(queryFormValues).length > 0)
) {
fetchParams = {
...queryFormValues,
...fetchParams,
};
}
return fetchParams;
};
const handleReload = () => {
// pages
let fetchParams = getAllFetchParams();
fetchData(fetchParams);
};
const handleSearchChange = (e) => {
const query = e.target.value;
setSearchQuery(query);
// 使用组件默认的search回调
setPagination({
...paginationState,
current: 1,
});
};
const checkRightHeader = (filters?: any, sorter?: any, search?: any) => {
const checkSorter = sorter || sorterState;
const checkFilters = filters || columnsState;
const checkSearch = search || columnsState;
const isSearch = Object.values(checkSearch).some((columnItem: any) => {
return !!(columnItem.type === 'search' && columnItem.value);
});
let isFilter = false;
if (filters) {
isFilter = Object.values(checkFilters).some((columnItem: any) => {
return columnItem && columnItem.length > 0;
});
} else {
isFilter = Object.values(checkFilters).some((columnItem: any) => {
return !!(
columnItem.type === 'filter' &&
columnItem.value &&
columnItem.value.length > 0
);
});
}
const isSorter = !!checkSorter.column;
const res = isSearch || isFilter || isSorter;
setShowRightHeader(res);
};
const isSearch = Object.values(columnsState).some((columnItem: any) => {
return !!(columnItem.type === 'search' && columnItem.value);
});
const isFilter = Object.values(columnsState).some((columnItem: any) => {
return !!(
columnItem.type === 'filter' &&
columnItem.value &&
columnItem.value.length > 0
);
});
useImperativeHandle(ref, () => ({
handleReload,
goToFirstPage: () => {
setPagination({
...paginationState,
current: 1,
});
},
}));
const renderColumns = () => {
return props.columns.map((columnItem) => {
const currentItem = _.cloneDeep(columnItem);
// filter
if (currentItem.apiFilter) {
currentItem.filterIcon = () => <FilterOutlined />;
currentItem.filteredValue =
columnsState[columnItem.dataIndex as string].value;
}
// // sort
if (currentItem.apiSorter) {
currentItem.sortOrder =
sorterState.columnKey === currentItem.dataIndex && sorterState.order;
}
// Search
if (currentItem.apiSearch) {
currentItem.filterIcon = () => <SearchOutlined />;
currentItem.onFilterDropdownVisibleChange = (visible: boolean) => {
if (
visible &&
filterSearchInputRef.current &&
filterSearchInputRef.current.select
) {
setTimeout(() => {
if (
filterSearchInputRef.current &&
filterSearchInputRef.current.select
) {
filterSearchInputRef.current.select();
}
return null;
});
}
};
currentItem.filterDropdown = ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
clearFiltersRef.current[currentItem.dataIndex as string] = {
clearFilters,
};
return (
<div style={{ padding: 8 }}>
<Input
data-testid='filter-search-input'
autoFocus={true}
ref={(node) => {
filterSearchInputRef.current = node;
}}
placeholder={t('table.filter.search.placeholder')}
value={selectedKeys && selectedKeys[0]}
onChange={(e) => {
if (setSelectedKeys) {
return setSelectedKeys(
e.target.value ? [e.target.value] : [],
);
}
return [];
}}
onPressEnter={() =>
handleFilterSearch(
selectedKeys,
confirm,
currentItem.dataIndex as string,
)
}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Button
type='primary'
onClick={() =>
handleFilterSearch(
selectedKeys,
confirm,
currentItem.dataIndex as string,
)
}
icon='search'
size='small'
data-testid='search-btn-ok'
style={{ width: 90, marginRight: 8 }}
>
{t('table.filter.search.btn.ok')}
</Button>
<Button
onClick={() =>
handleFilterSearchReset(clearFilters, currentItem.dataIndex)
}
size='small'
style={{ width: 90 }}
>
{t('table.filter.search.btn.cancel')}
</Button>
</div>
);
};
let searchWords: any[] = [];
const tmpStateValue =
columnsState[currentItem.dataIndex as string].value;
if (typeof tmpStateValue === 'string') {
searchWords = [tmpStateValue];
}
if (
Array.isArray(tmpStateValue) &&
typeof tmpStateValue[0] === 'string'
) {
searchWords = [tmpStateValue[0]];
}
currentItem.onFilter = (value, record: any) => {
return record[currentItem.dataIndex as string]
.toString()
.toLowerCase()
.includes(value.toLowerCase());
};
if (!currentItem.render) {
currentItem.render = (value, row, index) => {
if (currentItem.searchRender) {
return currentItem.searchRender(
value,
row,
index,
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={searchWords}
autoEscape
textToHighlight={String(value)}
/>,
);
} else {
return (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={searchWords}
autoEscape
textToHighlight={String(value)}
/>
);
}
};
}
}
return currentItem;
});
};
const renderRightHeader = (params) => {
if (!showFilter) {
return null;
}
return (
<>
<div>
<b
style={{
marginTop: 4,
display: 'inline-block',
}}
>
{t('table.filter.header.title')}
</b>
</div>
{(isSearch || isFilter) &&
Object.values(columnsState as IColumnsReducerState).map(
(columnValue) => {
if (columnValue.type === 'search' && columnValue.value) {
return (
<div
key={`search-header-${columnValue.dataIndex}`}
className={`${params.headerClsPrefix}-item`}
>
<Tooltip
title={
<span>
{columnValue.title}
{':'}
{columnValue.value}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={() => handleFilterSearchClear(columnValue)}
>
<span className='table-header-item-btn-content'>
{columnValue.title}
{':'}
{columnValue.value}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
);
}
if (
columnValue.type === 'filter' &&
columnValue.value &&
columnValue.value.length > 0
) {
const mappings = columnsMap[columnValue.dataIndex]
? columnsMap[columnValue.dataIndex].filters
: [];
const mapping = mappings.reduce((acc, cur) => {
return {
...acc,
[cur.value]: [cur.text],
};
}, {});
const newValues = columnValue.value.map((valItem) => {
return mapping && mapping[valItem]
? mapping[valItem]
: valItem;
});
return (
<div
key={`search-header-${columnValue.dataIndex}`}
className={`${params.headerClsPrefix}-item`}
>
<Tooltip
title={
<span>
{columnValue.title}
{':'}
{newValues.join(',')}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={() => handleFilterClear(columnValue)}
>
<span className='table-header-item-btn-content'>
{columnValue.title}
{':'}
{newValues.join(',')}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
);
}
return null;
},
)}
{sorterState.columnKey && sorterState.column && (
<div className={`${params.headerClsPrefix}-item`}>
<Tooltip
title={
<span>
{columnsMap[sorterState.columnKey].title}
{`: ${sorterNames[sorterState.order as TSorterNames]}`}
</span>
}
>
<Button
size='small'
className='table-header-item-btn'
onClick={handleSortClear}
>
<span className='table-header-item-btn-content'>
{columnsMap[sorterState.columnKey].title}
{`: ${sorterNames[sorterState.order as TSorterNames]}`}
</span>
<CloseOutlined />
</Button>
</Tooltip>
</div>
)}
<div className={`${params.headerClsPrefix}-item`}>
<Button
size='small'
type='link'
data-testid='btn-clearall'
onClick={handleClearAll}
>
{t('table.filter.header.btn.clear')}
</Button>
</div>
</>
);
};
const renderSearch = () => {
return (
<Tooltip placement='topLeft' title={searchPlaceholder}>
<Input
data-testid='search-input'
prefix={
reloadBtnPos === 'right' && (
<SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />
)
}
suffix={
reloadBtnPos === 'left' && (
<SearchOutlined style={{ color: 'rgba(0,0,0,.25)' }} />
)
}
allowClear={true}
value={searchQuery}
onChange={handleSearchChange}
placeholder={searchPlaceholder}
// suffix={<Icon onClick={handleSearchClick} type="search" />}
/>
</Tooltip>
);
};
const renderReloadBtn = () => {
if (reloadBtnType === 'icon') {
const reloadBtnCls = classNames({
[`${prefixCls}-header-loadbtn`]: true,
[`${prefixCls}-header-loadbtn-icon`]: true,
[`${prefixCls}-header-loadbtn-right`]: reloadBtnPos === 'right',
[`${prefixCls}-header-loadbtn-left`]: reloadBtnPos === 'left',
});
return (
<ReloadOutlined
onClick={handleReload}
spin={loading}
className={reloadBtnCls}
type='reload'
/>
);
}
if (reloadBtnType === 'btn') {
const reloadBtnCls = classNames({
[`${prefixCls}-header-loadbtn`]: true,
[`${prefixCls}-header-loadbtn-btn`]: true,
[`${prefixCls}-header-loadbtn-right`]: reloadBtnPos === 'right',
[`${prefixCls}-header-loadbtn-left`]: reloadBtnPos === 'left',
});
return (
<Button
className={reloadBtnCls}
loading={loading}
icon={<ReloadOutlined />}
data-testid='reload-btn'
onClick={handleReload}
/>
);
}
};
return (
<ConfigProvider {...props.antConfig}>
<div className={tableClassName} style={props.style}>
{queryFormColumns && (
<div className={tableQueryCls}>
<QueryForm
onChange={
isQuerySearchOnChange
? handleQueryFormChange
: handleQueryFormInit
}
onReset={handleQueryFormReset}
onSearch={handleQuerySearch}
columns={queryFormColumns}
showOptionBtns={showQueryOptionBtns}
showCollapseButton={showQueryCollapseButton}
{...queryFormProps}
/>
</div>
)}
<div className={tableBodyCls}>
{!!props.tableTitle && <h3> {props.tableTitle} </h3>}
{props.customHeader && (
<div
data-testid='custom-header'
className={`${prefixCls}-header-custom`}
>
{props.customHeader}
</div>
)}
{showFullSearch && (
<Row type='flex' className={`${prefixCls}-header-search`}>
{showSearch ? renderSearch() : <div></div>}
{showReloadBtn2SearchRight && renderReloadBtn()}
</Row>
)}
{/* 自定义格式 */}
{filterType === 'flex' && (
<div
style={{
display: 'flex',
justifyContent: 'flex-start',
marginBottom: '10px',
}}
>
{showReloadBtn2FilterLeft && renderReloadBtn()}
{props.leftHeader}
</div>
)}
{filterType === 'none' && searchPos !== 'right' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
>
{props.leftHeader}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'none' && showSearch && searchPos === 'right' && (
<Row className={`${prefixCls}-header-filter`}>
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
span={14}
>
{showReloadBtn2FilterLeft && renderReloadBtn()}
{props.leftHeader}
</Col>
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-right`}
span={10}
>
{renderSearch()}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'line' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-line`}
span={24}
>
{showRightHeader &&
renderRightHeader({
headerClsPrefix: `${prefixCls}-header-filter-line`,
})}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
{filterType === 'half' && (
<Row className={`${prefixCls}-header-filter`}>
{showReloadBtn2FilterLeft && renderReloadBtn()}
<Col
data-testid='left-header'
className={classNames(
`${prefixCls}-header-filter-left`,
props.leftHeader !== undefined &&
`${prefixCls}-header-filter-left-minh`,
)}
span={12}
>
{props.leftHeader}
</Col>
<Col
data-testid='right-header'
className={`${prefixCls}-header-filter-right`}
span={12}
>
{showRightHeader &&
renderRightHeader({
headerClsPrefix: `${prefixCls}-header-filter-right`,
})}
</Col>
{showReloadBtn2FilterRight && renderReloadBtn()}
</Row>
)}
<div className={`${prefixCls}-table-wrapper`}>
<div className={tableContentClassName}>
<Table
loading={loading}
columns={renderColumns()}
dataSource={dataSource}
bordered={false}
rowSelection={rowSelection}
onChange={handleTableChange}
pagination={{
showTotal: showTotal,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: pageSizeOptions,
...props.pagination,
...paginationState,
}}
{...antProps}
/>
{rowSelection && selectRowNum !== undefined && selectRowNum > 0 && (
<div className={`${prefixCls}-table-content-select-num`}>
{t('table.select.num')}
{selectRowNum}
</div>
)}
</div>
</div>
</div>
</div>
</ConfigProvider>
);
}
Example #8
Source File: ChartDraggableElementField.tsx From datart with Apache License 2.0 | 4 votes |
ChartDraggableElementField: FC<{
modalSize;
config;
columnConfig;
ancestors;
aggregation;
availableSourceFunctions;
onConfigChanged;
handleOpenActionModal;
}> = memo(
({
modalSize,
config,
columnConfig,
ancestors,
aggregation,
availableSourceFunctions,
onConfigChanged,
handleOpenActionModal,
}) => {
const renderActionExtensionMenu = (uid: string, type: string, category) => {
return (
<ChartDataConfigSectionActionMenu
uid={uid}
type={type}
category={category}
ancestors={ancestors}
config={config}
modalSize={modalSize}
availableSourceFunctions={availableSourceFunctions}
onConfigChanged={onConfigChanged}
onOpenModal={handleOpenActionModal}
/>
);
};
const enableActionsIcons = col => {
const icons = [] as any;
if (col.alias) {
icons.push(<DiffOutlined key="alias" />);
}
if (col.sort) {
if (col.sort.type === SortActionType.ASC) {
icons.push(<SortAscendingOutlined key="sort" />);
}
if (col.sort.type === SortActionType.DESC) {
icons.push(<SortDescendingOutlined key="sort" />);
}
}
if (col.format) {
icons.push(<FormatPainterOutlined key="format" />);
}
if (col.aggregate) {
icons.push(<GroupOutlined key="aggregate" />);
}
if (col.filter) {
icons.push(<FilterOutlined key="filter" />);
}
if (col.color) {
icons.push(<BgColorsOutlined key="color" />);
}
if (col.size) {
icons.push(<FontSizeOutlined key="size" />);
}
return icons;
};
return (
<Dropdown
key={columnConfig.uid}
disabled={!config?.actions}
destroyPopupOnHide={true}
overlay={renderActionExtensionMenu(
columnConfig.uid!,
columnConfig.type,
columnConfig.category,
)}
overlayClassName="datart-data-section-dropdown"
trigger={['click']}
>
<div>
{config?.actions && <DownOutlined style={{ marginRight: '10px' }} />}
<span>
{aggregation === false
? columnConfig.colName
: getColumnRenderName(columnConfig)}
</span>
<div style={{ display: 'inline-block', marginLeft: '5px' }}>
{enableActionsIcons(columnConfig)}
</div>
</div>
</Dropdown>
);
},
)
Example #9
Source File: index.tsx From amiya with MIT License | 4 votes |
export default function Demo(props: IProps) {
const {
api,
fields,
topFields = [],
extendFields = [],
tabs = [],
tabsApi,
children,
tableRef: otherTableRef,
topFormRef: otherTopFormRef,
searchValues: otherSearchValues,
...otherProps
} = props
const tableRef = useRef<any>()
const topFormRef = useRef<any>()
/** 筛选弹窗是否可见 */
const [extendVisible, setExtendVisible] = useState(false)
/** 筛选弹窗的值 */
const [extendValues, setExtendValues] = useState<FormValues>({})
/** 顶部 Tab 选项 */
const [tabOptions, setTabOptions] = useState(tabs || [])
/** 当前命中的 Tab */
const [activeTab, setActiveTab] = useState<string>(tabOptions[0].value)
/** 查询数据 */
const [searchValues, setSearchValues] = useState<FormValues>({})
if (otherTableRef) {
otherTableRef.current = tableRef.current
}
if (otherTopFormRef) {
otherTopFormRef.current = topFormRef.current
}
const beforeSearch = (values: FormValues) => {
setSearchValues({ ...values.search })
return values
}
const handleLoad = () => {
loadTabCount()
}
const loadTabCount = () => {
if (!tabsApi) {
return
}
tabsApi().then((data: any) => {
let newTabOptions = [...tabs]
data.forEach((row: AnyKeyProps) => {
let option = newTabOptions.find(option => option.value === row.status)
if (option) {
option.count = row.count
}
})
setTabOptions(newTabOptions)
})
}
return (
<div className="table">
<AySearchTable
fields={fields}
api={api}
filterData={filterData}
useOriginPagination={false}
rowKey="key"
onLoad={handleLoad}
tableExtend={{ bordered: false }}
searchExtend={{ inline: true }}
beforeSearch={beforeSearch}
extendSearchParams={{
...extendValues,
...otherSearchValues,
activeTab
}}
before={
<Card className="top-form-card" bodyStyle={{ padding: 0 }}>
<AyForm style={{ marginTop: 20 }} fields={topFields} ref={topFormRef} />
</Card>
}
center={
<div style={{ marginBottom: 12 }}>
<AyButton icon={<FilterOutlined />} onClick={() => setExtendVisible(true)}>
筛选
</AyButton>
<div style={{ marginTop: 12 }}>
<SearchData
extendValues={extendValues}
searchValues={searchValues}
keyMap={{
keyword: searchValues['keywordType'] === 1 ? '商品名称' : 'SKU'
}}
searchFields={fields}
extendFields={extendFields}
noVisibleKeys={['keywordType', 'activeTab', ...Object.keys(otherSearchValues)]}
onRemoveSearchValue={key => {
tableRef.current.getSearchRef().setFieldsValue({
[key]: undefined
})
tableRef.current.reset()
}}
onRemoveExtendValue={key => {
setExtendValues({
...extendValues,
[key]: undefined
})
tableRef.current.reset()
}}
clearAll={() => {
tableRef.current.getSearchRef().resetFields()
setExtendValues({})
tableRef.current.reset()
}}
/>
</div>
</div>
}
scrollX={1600}
ref={tableRef}
title={
<Tabs
value={activeTab}
options={tabOptions}
onChange={value => {
setActiveTab(value)
tableRef.current.reset()
}}
/>
}
{...(otherProps as any)}
>
{children}
</AySearchTable>
<AyDialogForm
title="筛选"
visible={extendVisible}
onClose={() => setExtendVisible(false)}
onSuccess={({ values }) => {
setExtendValues(values)
tableRef.current.reset()
}}
fields={extendFields}
initialValues={{
...extendValues
}}
addApi={extendData => Promise.resolve(extendData) as any}
formExtend={{ className: 'full-width' }}
/>
</div>
)
}