From 33df825b732dd9cb6a5a5ce9f72ccd157bd17f02 Mon Sep 17 00:00:00 2001 From: jialin Date: Wed, 30 Jul 2025 17:45:44 +0800 Subject: [PATCH] feat: add card list component --- src/components/page-tools/index.tsx | 6 +- src/components/templates/card-list.tsx | 105 +++++++++++++++++++++++++ src/components/templates/card.tsx | 44 +++++++++++ src/pages/llmodels/user-models.tsx | 21 ++++- 4 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 src/components/templates/card-list.tsx create mode 100644 src/components/templates/card.tsx diff --git a/src/components/page-tools/index.tsx b/src/components/page-tools/index.tsx index 1d9ca313..48480e3f 100644 --- a/src/components/page-tools/index.tsx +++ b/src/components/page-tools/index.tsx @@ -138,12 +138,12 @@ export const FilterBar: React.FC = (props) => { icon={} danger onClick={handleDeleteByBatch} - disabled={!rowSelection.selectedRowKeys.length} + disabled={!rowSelection?.selectedRowKeys?.length} > {intl?.formatMessage?.({ id: 'common.button.delete' })} - {rowSelection.selectedRowKeys.length > 0 && ( - ({rowSelection.selectedRowKeys?.length}) + {rowSelection?.selectedRowKeys?.length > 0 && ( + ({rowSelection?.selectedRowKeys?.length}) )} diff --git a/src/components/templates/card-list.tsx b/src/components/templates/card-list.tsx new file mode 100644 index 00000000..97cd2cb6 --- /dev/null +++ b/src/components/templates/card-list.tsx @@ -0,0 +1,105 @@ +import breakpoints from '@/config/breakpoints'; +import { Col, FloatButton, Row, Spin } from 'antd'; +import _ from 'lodash'; +import ResizeObserver from 'rc-resize-observer'; +import React, { useCallback } from 'react'; +import styled from 'styled-components'; + +const SpinWrapper = styled.div` + width: 100%; + position: absolute; + display: flex; + align-items: center; + justify-content: center; + top: 0; + left: 0; + max-height: 400px; + right: 0; +`; + +interface CatalogListProps { + dataList: any[]; + loading: boolean; + activeId: number; + isFirst: boolean; + onDeploy: (data: any) => void; + renderItem: (data: any) => React.ReactNode; + Skeleton: React.ComponentType<{ span: number }>; +} + +const InnerSkeleton = (Skeleton: React.ComponentType<{ span: number }>) => { + return (props: any) => { + return ; + }; +}; + +const ListSkeleton: React.FC<{ + span: number; + loading: boolean; + isFirst: boolean; + Skeleton: React.ComponentType<{ span: number }>; +}> = ({ span, loading, isFirst, Skeleton }) => { + const CatalogSkeleton = InnerSkeleton(Skeleton); + return ( +
+ {loading && ( + + + {isFirst && } + + + )} +
+ ); +}; + +const CardList: React.FC = (props) => { + const { dataList, loading, isFirst, Skeleton, renderItem } = props; + const [span, setSpan] = React.useState(8); + + const getSpanByWidth = (width: number) => { + if (width < breakpoints.md) return 24; + if (width < breakpoints.lg) return 12; + return 8; + }; + + const handleResize = useCallback( + _.throttle((size: { width: number; height: number }) => { + setSpan(getSpanByWidth(size.width)); + }, 100), + [] + ); + + return ( +
+ +
+ + {dataList.map((item: any, index) => { + return ( + + {renderItem?.(item)} + + ); + })} + + +
+
+ +
+ ); +}; + +export default CardList; diff --git a/src/components/templates/card.tsx b/src/components/templates/card.tsx new file mode 100644 index 00000000..6455bce1 --- /dev/null +++ b/src/components/templates/card.tsx @@ -0,0 +1,44 @@ +import classNames from 'classnames'; +import React from 'react'; +import styled from 'styled-components'; + +interface CardProps { + height?: string | number; + className?: string; + children?: React.ReactNode; +} + +const CardWrapper = styled.div` + overflow: hidden; + display: flex; + padding: 16px 20px; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + border: 1px solid var(--ant-color-border); + border-radius: var(--border-radius-base); + cursor: pointer; + width: 100%; + &:hover { + background-color: var(--ant-color-fill-tertiary); + } + + &.active { + background-color: var(--ant-color-fill-tertiary); + } +`; + +const Card: React.FC = (props) => { + const { className, height, children } = props; + + return ( + + {children} + + ); +}; + +export default Card; diff --git a/src/pages/llmodels/user-models.tsx b/src/pages/llmodels/user-models.tsx index a009ef51..d23bb2af 100644 --- a/src/pages/llmodels/user-models.tsx +++ b/src/pages/llmodels/user-models.tsx @@ -1,9 +1,17 @@ +import { FilterBar } from '@/components/page-tools'; import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; import React from 'react'; const UserModels: React.FC = () => { const intl = useIntl(); + + const handleSearch = () => { + // Implement search functionality here + }; + + const handleInputChange = () => {}; + return ( { }} extra={[]} > - {intl.formatMessage({ id: 'menu.models.userModels' })} + {}} + handleSearch={handleSearch} + width={{ input: 200 }} + > ); };